Class: Mageo::Triangle

Inherits:
Object
  • Object
show all
Defined in:
lib/mageo/triangle.rb

Overview

3次元空間中の3角形を表現するクラス。

法線ベクトル( Mageo::Vector3D クラスインスタンス )を返すメソッドは定義しない。

法線ベクトルは2通りの方向を取りうるため。
initialize 時に点の指定の順序を決めることで定義はできるが、
そうすると簡潔性が損なわれる。

Defined Under Namespace

Classes: InitializeError, LinearException, NoIntersectionError, TypeError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(vertices) ⇒ Triangle

An argument ‘vertices’ can be Array of 3 items, Vector of 3 items, or Mageo::Vector3D class instance, which have [] and map methods. 当面は Array を前提とする。 座標が整数で入っていたとしても内部的には Float に変換して使用する。 3点が1直線上に並んでいて三角形を囲まない場合は 例外 Mageo::Triangle::LinearException を投げる。

Raises:



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/mageo/triangle.rb', line 25

def initialize( vertices )
  raise InitializeError unless vertices.methods.include?( :size )
  raise InitializeError if vertices.size != 3
  vertices.each do |pos|
    raise InitializeError if pos.size != 3
    raise InitializeError unless pos.methods.include?( :[] )
    raise InitializeError unless pos.methods.include?( :map )
  end
  @vertices = vertices.map do |pos| 
    ( pos.map { |i| i.to_f }) . to_v3d
  end

  #Checking on linear.
  edge1 = @vertices[1] - @vertices[0]
  edge2 = @vertices[2] - @vertices[0]
  if ( Mageo::Vector3D[0.0, 0.0, 0.0] == Mageo::Vector3D.vector_product( edge1, edge2 ))
    raise LinearException
  end

end

Instance Attribute Details

#verticesObject (readonly)

Returns the value of attribute vertices.



11
12
13
# File 'lib/mageo/triangle.rb', line 11

def vertices
  @vertices
end

Instance Method Details

#==(other) ⇒ Object

3つの頂点の座標が順序通りに対応すれば真を返す。



193
194
195
196
197
198
# File 'lib/mageo/triangle.rb', line 193

def ==(other)
  vertices.size.times do |i|
    return false unless @vertices[i] == other.vertices[i]
  end
  return true
end

#edgesObject



200
201
202
203
204
205
206
# File 'lib/mageo/triangle.rb', line 200

def edges
  results = []
  results << Mageo::Segment.new(@vertices[0], @vertices[1])
  results << Mageo::Segment.new(@vertices[1], @vertices[2])
  results << Mageo::Segment.new(@vertices[2], @vertices[0])
  return results
end

#equivalent?(other, tolerance = 0.0) ⇒ Boolean Also known as: eql?

3つの頂点の座標が順不同で対応すれば真を返す。 other が Mageo::Triangle クラス以外のインスタンスなら例外 Triangle::TypeError を投げる。 MEMO: 当初 eql? という名前を付けていたが、 これは hash メソッドと関連があるので危険。 よって別の名前の equivalent? というメソッド名にした。 しかし eql? に依存したコードが残っているので当面 alias を残す。 そのうち obsolete する。

Returns:

  • (Boolean)

Raises:



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/mageo/triangle.rb', line 172

def equivalent?(other, tolerance = 0.0)
  raise TypeError unless other.class == Mageo::Triangle

  vertices.each do |v_self|
    if (other.vertices.find{|v_other| v_self.equal_in_delta?(v_other, tolerance) })
      next
    else
      return false 
    end
  end
  #return false unless other.vertices.include?(v)
  return true
  
  #vertices.each do |v|
  # return false unless other.vertices.include?(v)
  #end
  #return true
end

#include?(pos, tolerance) ⇒ Boolean

3点で張られる面上にあり、三角形の内側にあれば true を返す。 pos が Mageo::Vector3D クラスインスタンスでなければ例外。 ただし、面の法線方向には tolerance だけの許容値を設ける。 計算誤差の問題のため、これを設定しないと殆ど真とならない。

Returns:

  • (Boolean)

Raises:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/mageo/triangle.rb', line 70

def include?(pos, tolerance)
  raise TypeError if pos.class != Mageo::Vector3D

  axes = internal_axes
  #一次独立チェックは initialize 時にされている筈。

  pos   = (pos - @vertices[0]).to_v3d
  internal_pos = pos.internal_coordinates(axes)
  return false if internal_pos[2].abs > tolerance #面の外にあれば false
  return false if (internal_pos[0] < 0.0 )
  return false if (1.0 < internal_pos[0])
  return false if (internal_pos[1] < 0.0 )
  return false if (1.0 < internal_pos[1])
  return false if (1.0 < internal_pos[0] + internal_pos[1])
  #たしざんで 1 いかとか。
  return true
end

#intersection(segment, tolerance) ⇒ Object

三角形を含む面と線分の交点を返す。 交点がない、無数にある、三角形の内側にない、という場合には 例外 NoIntersectionError を返す。



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/mageo/triangle.rb', line 100

def intersection(segment, tolerance)
  # 平行のとき、例外。
  raise NoIntersectionError if parallel_segment?(segment)

  # 面内の直線のとき、例外。
  endpoints_v3di = segment.endpoints.map do |v|
    (v - @vertices[0]).internal_coordinates(internal_axes)
  end
  if ( (endpoints_v3di[0][2] == 0.0) && (endpoints_v3di[1][2] == 0.0) )
    raise NoIntersectionError
  end

  # 面と直線の交点を求める。
  # endpoints_v3di をベクトルと見たとき、 c 軸成分が 0 になるのは
  # ベクトルの何倍か?
  # ゼロ割りになる条件は、ここまでに弾いた条件に含まれる筈。
  c_0 = endpoints_v3di[0][2]
  c_1 = endpoints_v3di[1][2]
  factor = c_0 / (c_0 - c_1)
  result = (segment.endpoints[0] + (segment.to_v3d) * factor)

  # 交点が線分上にあるか?すなわち両端点が面を挟んでいるか?
  raise NoIntersectionError if c_0 * c_1 > 0

  # 交点が三角形の内側にあるか?
  if (! include?(result, tolerance))
    raise NoIntersectionError
  end

  return result
end

#normal_vectorObject

法線ベクトルの1つを返す。 法線ベクトルは正反対の方向を示す 2つがあるが、 内部的には頂点の順が右ねじとなる方向(正確には外積の定義される方向) のものが選ばれる。 また、長さが1に規格化されたものが返される。



155
156
157
158
159
160
161
162
# File 'lib/mageo/triangle.rb', line 155

def normal_vector
  edge1 = (@vertices[1] - @vertices[0])
  edge2 = (@vertices[2] - @vertices[1])
  vec = edge1.exterior_product( edge2 )
  normal = vec * (1.0/vec.r)

  return normal
end

#parallel_segment?(segment) ⇒ Boolean

線分と並行であることを判定する。 線分が三角形の面内の場合は false になる。

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/mageo/triangle.rb', line 134

def parallel_segment?(segment)
  #p segment.endpoints
  t = segment.endpoints.map{|v|
    v.internal_coordinates(internal_axes)
  }

  # 傾いてるときは常に false
  return false if t[0][2] != t[1][2]

  # 傾きがない場合、面に含まれていれば false
  return false if t[0][2] == 0.0

  # 残った場合が true
  return true
end

#same_side?(pos0, pos1) ⇒ Boolean

引数で与えられた 2 つの座標が、三角形の面に対して同じ側にあれば true を返す。 どちらか、もしくは両方が、面上の点(当然頂点、辺上を含む)であれば必ず false を返す。

Returns:

  • (Boolean)

Raises:



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/mageo/triangle.rb', line 48

def same_side?( pos0, pos1 )
  raise TypeError if pos0.class != Mageo::Vector3D
  raise TypeError if pos1.class != Mageo::Vector3D

  edge1 = @vertices[1] - @vertices[0]
  edge2 = @vertices[2] - @vertices[0]
  pos0  = pos0.to_v3d - @vertices[0]
  pos1  = pos1.to_v3d - @vertices[0]

  triple_product_pos0 = Mageo::Vector3D.scalar_triple_product( edge1, edge2, pos0 )
  triple_product_pos1 = Mageo::Vector3D.scalar_triple_product( edge1, edge2, pos1 )
  if triple_product_pos0 * triple_product_pos1 > 0
    return true
  else
    return false
  end
end