lib/triangle.rb in gmath3D-0.2.4 vs lib/triangle.rb in gmath3D-0.2.5

- old
+ new

@@ -1,281 +1,281 @@ -require 'gmath3D' - -module GMath3D - # - # Triangle represents a three edged finite plane on 3D space. - # - class Triangle < Geom -public - attr_accessor :vertices - - include BoxAvailable - - # [Input] - # _vertex1_, _vertex2_, _vertex3_ should be Vector3. - # [Output] - # return new instance of Triangle. - def initialize(vertex1 = Vector3.new(), vertex2 = Vector3.new(1,0,0), vertex3 = Vector3.new(0,1,0)) - Util3D.check_arg_type(::Vector3, vertex1) - Util3D.check_arg_type(::Vector3, vertex2) - Util3D.check_arg_type(::Vector3, vertex3) - super() - @vertices = Array.new([vertex1, vertex2, vertex3]) - end - - def initialize_copy( original_obj ) - @vertices = Array.new(original_obj.vertices.size) - for i in 0..@vertices.size-1 - @vertices[i] = original_obj.vertices[i].dup - end - end - - # [Input] - # _rhs_ is Line. - # [Output] - # return true if rhs equals myself. - def ==(rhs) - return false if rhs == nil - return false if( !rhs.kind_of?(Triangle) ) - return false if(@vertices.size != rhs.vertices.size) - for i in 0..@vertices.size-1 - return false if(@vertices[i] != rhs.vertices[i]) - end - return true - end - - def to_s - "Triangle[#{@vertices[0].to_element_s}, #{@vertices[1].to_element_s}, #{@vertices[2].to_element_s}]" - end - - # [Input] - # _parameter_ should be three element Array of Numeric. - # [Output] - # return point on triangle at parameter position as Vector3. - def point( parameter ) - Util3D.check_arg_type(::Array, parameter ) - # TODO Argument check - return self.vertices[0]*parameter[0] + self.vertices[1]*parameter[1] + self.vertices[2]*parameter[2] - end - - # [Output] - # return edges as three element Array of Vector3. - def edges - return_edges = Array.new(3) - return_edges[0] = FiniteLine.new(self.vertices[0], self.vertices[1]) - return_edges[1] = FiniteLine.new(self.vertices[1], self.vertices[2]) - return_edges[2] = FiniteLine.new(self.vertices[2], self.vertices[0]) - return return_edges - end - - # [Output] - # return area as Numeric. - def area - vec1 = vertices[1] - vertices[0] - vec2 = vertices[2] - vertices[0] - outer_product = vec1.cross(vec2) - return outer_product.length / 2.0 - end - - # [Output] - # return center point as Vector3. - def center - return vertices.avg - end - - # [Output] - # return normal vector as Vector3. - def normal - vec1 = self.vertices[1] - self.vertices[0] - vec2 = self.vertices[2] - self.vertices[0] - return (vec1.cross(vec2).normalize) - end - - # [Input] - # _vertex_index_ should be 0..2. - # [Output] - # return angle as Numeric(radian). - def angle( vertex_index ) - return nil if(vertex_index < 0 || vertex_index > 2) - vert1 = self.vertices[vertex_index] - vert2 = self.vertices[(vertex_index+1)%3] - vert3 = self.vertices[(vertex_index+2)%3] - vec1 = vert2 - vert1 - vec2 = vert3 - vert1 - vec1.angle(vec2) - end - - # [Output] - # return normal vector reversed triangle - def reverse - return Triangle.new(@vertices[0], @vertices[2], @vertices[1]) - end - - # [Input] - # _check_point_ should be Vector3. - # [Output] - # return barycentric_coordinate on check_point as three element Array of Numeric. - def barycentric_coordinate( check_point ) - Util3D.check_arg_type(::Vector3, check_point) - - v0 = @vertices[0] - v1 = @vertices[1] - v2 = @vertices[2] - - d1 = v1 - v0 - d2 = v2 - v1 - n = d1.cross(d2); - if((n.x).abs >= (n.y).abs && (n.x).abs >= (n.z).abs) - uu1 = v0.y - v2.y; - uu2 = v1.y - v2.y; - uu3 = check_point.y - v0.y; - uu4 = check_point.y - v2.y; - vv1 = v0.z - v2.z; - vv2 = v1.z - v2.z; - vv3 = check_point.z - v0.z; - vv4 = check_point.z - v2.z; - elsif((n.y).abs >= (n.z).abs) - uu1 = v0.z - v2.z; - uu2 = v1.z - v2.z; - uu3 = check_point.z - v0.z; - uu4 = check_point.z - v2.z; - vv1 = v0.x - v2.x; - vv2 = v1.x - v2.x; - vv3 = check_point.x - v0.x; - vv4 = check_point.x - v2.x; - else - uu1 = v0.x - v2.x; - uu2 = v1.x - v2.x; - uu3 = check_point.x - v0.x; - uu4 = check_point.x - v2.x; - vv1 = v0.y - v2.y; - vv2 = v1.y - v2.y; - vv3 = check_point.y - v0.y; - vv4 = check_point.y - v2.y; - end - - denom = vv1 * uu2 - vv2* uu1 - if(denom == 0.0) - return nil - end - b = Array.new(3) - oneOverDenom = 1.0 / denom ; - b[0] = (vv4*uu2 - vv2*uu4) * oneOverDenom; - b[1] = (vv1*uu3 - vv3*uu1) * oneOverDenom; - b[2] = 1.0 - b[0] - b[1]; - return b; - end - - # [Input] - # _target_ shold be Vector3 or Line or Plane. - # [Output] - # [In case _target_ is Vector3] - # return "distance, point on triangle" as [Numeric, Vector3]. - # [In case _target_ is Line] - # return "distance, point on tirangle, point on line, parameter on line" as [Numeric, Vector3, Vector3, Numeric]. - # [In case _target_ is Plane] - # return "distance, intersect_line(or closet edge), point_on_triangle, point_on_plane" as [Numeric, Vector3, Vector3, Vector3]. - def distance(target) - # with Point - if(target.kind_of?(Vector3)) - return distance_to_point(target) - elsif(target.kind_of?(Line)) - #with Line - return distance_to_line(target) - elsif(target.kind_of?(Plane)) - #with Plane - return distance_to_plane(target) - end - Util3D.raise_argurment_error(target) - end - - # [Input] - # _check_point_ shold be Vector3. - # [Output] - # return true if triangle contains _check_point_. - def contains?( check_point ) - Util3D.check_arg_type(Vector3, check_point ) - plane = Plane.new( vertices[0], self.normal) - distance, projected_point = plane.distance(check_point) - return false if( distance > self.tolerance ) - g_coord = self.barycentric_coordinate(check_point) - g_coord.each do |item| - return false if( item < 0 or 1 < item) - end - return true - end - -private - def distance_to_point(target_point) - plane = Plane.new( vertices[0], self.normal) - distance, projected_point = plane.distance(target_point) - if( self.contains?(projected_point)) - return distance, projected_point - end - #check distance to FiniteLines - finite_lines = self.edges - return FiniteLine.ary_distanc_to_point(finite_lines, target_point) - end - - def distance_to_line(target_line) - plane = Plane.new( vertices[0], self.normal ) - distance, point_on_plane, parameter_on_line = plane.distance( target_line ) - if( point_on_plane == nil) - # parallel case - # check distance to FiniteLines - finite_lines = self.edges - distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target = - FiniteLine.ary_distance_to_line(finite_lines, target_line) - return distance, nil, nil, nil - end - if( self.contains?(point_on_plane) ) - return distance, point_on_plane, point_on_plane, parameter_on_line - end - # check distance to FiniteLines - finite_lines = self.edges - distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target = - FiniteLine.ary_distance_to_line(finite_lines, target_line) - return distance, point_on_edge, point_on_target, param_on_target - end - - def distance_to_plane(target_plane) - triangle_plane = Plane.new( vertices[0], self.normal ) - distance, intersect_line_each_plane = triangle_plane.distance( target_plane ) - if( intersect_line_each_plane == nil ) - return distance, nil, nil, nil - end - - # check distance from intersection and each edge. - distance_zero_count = 0 - distance_info = Array.new(0) - prallel_edge_ary = Array.new(0) - self.edges.each do |edge| - distance, point_on_edge, point_on_line = edge.distance( intersect_line_each_plane) - if point_on_edge != nil && point_on_line != nil - distance_info.push([distance, point_on_edge, point_on_line]) - if distance <= self.tolerance - distance_zero_count += 1 - end - else - prallel_edge_ary.push( edge ) - end - end - distance_info.sort!{|a,b| a[0] <=> b[0]} - # distance, intersect_line(or closet edge), point_on_triangle, point_on_plan - if (distance_zero_count == 2) - point1 = distance_info[0][1] - point2 = distance_info[1][1] - if point1.distance(point2) > self.tolerance - return 0.0, FiniteLine.new(point1, point2), nil, nil - end - return 0.0, nil, point1, point1 - elsif (distance_zero_count == 0) - distance, closest_point_on_plane = target_plane.distance(distance_info[0][1]) - if(distance_info[0][1] != distance_info[1][1]) - return distance, FiniteLine.new(distance_info[0][1], distance_info[1][1]), nil, nil - end - return distance, nil, distance_info[0][1], closest_point_on_plane - end - return 0.0, nil, nil, nil - end - end -end +require 'gmath3D' + +module GMath3D + # + # Triangle represents a three edged finite plane on 3D space. + # + class Triangle < Geom +public + attr_accessor :vertices + + include BoxAvailable + + # [Input] + # _vertex1_, _vertex2_, _vertex3_ should be Vector3. + # [Output] + # return new instance of Triangle. + def initialize(vertex1 = Vector3.new(), vertex2 = Vector3.new(1,0,0), vertex3 = Vector3.new(0,1,0)) + Util3D.check_arg_type(::Vector3, vertex1) + Util3D.check_arg_type(::Vector3, vertex2) + Util3D.check_arg_type(::Vector3, vertex3) + super() + @vertices = Array.new([vertex1, vertex2, vertex3]) + end + + def initialize_copy( original_obj ) + @vertices = Array.new(original_obj.vertices.size) + for i in 0..@vertices.size-1 + @vertices[i] = original_obj.vertices[i].dup + end + end + + # [Input] + # _rhs_ is Line. + # [Output] + # return true if rhs equals myself. + def ==(rhs) + return false if rhs == nil + return false if( !rhs.kind_of?(Triangle) ) + return false if(@vertices.size != rhs.vertices.size) + for i in 0..@vertices.size-1 + return false if(@vertices[i] != rhs.vertices[i]) + end + return true + end + + def to_s + "Triangle[#{@vertices[0].to_element_s}, #{@vertices[1].to_element_s}, #{@vertices[2].to_element_s}]" + end + + # [Input] + # _parameter_ should be three element Array of Numeric. + # [Output] + # return point on triangle at parameter position as Vector3. + def point( parameter ) + Util3D.check_arg_type(::Array, parameter ) + # TODO Argument check + return self.vertices[0]*parameter[0] + self.vertices[1]*parameter[1] + self.vertices[2]*parameter[2] + end + + # [Output] + # return edges as three element Array of Vector3. + def edges + return_edges = Array.new(3) + return_edges[0] = FiniteLine.new(self.vertices[0], self.vertices[1]) + return_edges[1] = FiniteLine.new(self.vertices[1], self.vertices[2]) + return_edges[2] = FiniteLine.new(self.vertices[2], self.vertices[0]) + return return_edges + end + + # [Output] + # return area as Numeric. + def area + vec1 = vertices[1] - vertices[0] + vec2 = vertices[2] - vertices[0] + outer_product = vec1.cross(vec2) + return outer_product.length / 2.0 + end + + # [Output] + # return center point as Vector3. + def center + return vertices.avg + end + + # [Output] + # return normal vector as Vector3. + def normal + vec1 = self.vertices[1] - self.vertices[0] + vec2 = self.vertices[2] - self.vertices[0] + return (vec1.cross(vec2).normalize) + end + + # [Input] + # _vertex_index_ should be 0..2. + # [Output] + # return angle as Numeric(radian). + def angle( vertex_index ) + return nil if(vertex_index < 0 || vertex_index > 2) + vert1 = self.vertices[vertex_index] + vert2 = self.vertices[(vertex_index+1)%3] + vert3 = self.vertices[(vertex_index+2)%3] + vec1 = vert2 - vert1 + vec2 = vert3 - vert1 + vec1.angle(vec2) + end + + # [Output] + # return normal vector reversed triangle + def reverse + return Triangle.new(@vertices[0], @vertices[2], @vertices[1]) + end + + # [Input] + # _check_point_ should be Vector3. + # [Output] + # return barycentric_coordinate on check_point as three element Array of Numeric. + def barycentric_coordinate( check_point ) + Util3D.check_arg_type(::Vector3, check_point) + + v0 = @vertices[0] + v1 = @vertices[1] + v2 = @vertices[2] + + d1 = v1 - v0 + d2 = v2 - v1 + n = d1.cross(d2); + if((n.x).abs >= (n.y).abs && (n.x).abs >= (n.z).abs) + uu1 = v0.y - v2.y; + uu2 = v1.y - v2.y; + uu3 = check_point.y - v0.y; + uu4 = check_point.y - v2.y; + vv1 = v0.z - v2.z; + vv2 = v1.z - v2.z; + vv3 = check_point.z - v0.z; + vv4 = check_point.z - v2.z; + elsif((n.y).abs >= (n.z).abs) + uu1 = v0.z - v2.z; + uu2 = v1.z - v2.z; + uu3 = check_point.z - v0.z; + uu4 = check_point.z - v2.z; + vv1 = v0.x - v2.x; + vv2 = v1.x - v2.x; + vv3 = check_point.x - v0.x; + vv4 = check_point.x - v2.x; + else + uu1 = v0.x - v2.x; + uu2 = v1.x - v2.x; + uu3 = check_point.x - v0.x; + uu4 = check_point.x - v2.x; + vv1 = v0.y - v2.y; + vv2 = v1.y - v2.y; + vv3 = check_point.y - v0.y; + vv4 = check_point.y - v2.y; + end + + denom = vv1 * uu2 - vv2* uu1 + if(denom == 0.0) + return nil + end + b = Array.new(3) + oneOverDenom = 1.0 / denom ; + b[0] = (vv4*uu2 - vv2*uu4) * oneOverDenom; + b[1] = (vv1*uu3 - vv3*uu1) * oneOverDenom; + b[2] = 1.0 - b[0] - b[1]; + return b; + end + + # [Input] + # _target_ shold be Vector3 or Line or Plane. + # [Output] + # [In case _target_ is Vector3] + # return "distance, point on triangle" as [Numeric, Vector3]. + # [In case _target_ is Line] + # return "distance, point on tirangle, point on line, parameter on line" as [Numeric, Vector3, Vector3, Numeric]. + # [In case _target_ is Plane] + # return "distance, intersect_line(or closet edge), point_on_triangle, point_on_plane" as [Numeric, Vector3, Vector3, Vector3]. + def distance(target) + # with Point + if(target.kind_of?(Vector3)) + return distance_to_point(target) + elsif(target.kind_of?(Line)) + #with Line + return distance_to_line(target) + elsif(target.kind_of?(Plane)) + #with Plane + return distance_to_plane(target) + end + Util3D.raise_argurment_error(target) + end + + # [Input] + # _check_point_ shold be Vector3. + # [Output] + # return true if triangle contains _check_point_. + def contains?( check_point ) + Util3D.check_arg_type(Vector3, check_point ) + plane = Plane.new( vertices[0], self.normal) + distance, projected_point = plane.distance(check_point) + return false if( distance > self.tolerance ) + g_coord = self.barycentric_coordinate(check_point) + g_coord.each do |item| + return false if( item < 0 or 1 < item) + end + return true + end + +private + def distance_to_point(target_point) + plane = Plane.new( vertices[0], self.normal) + distance, projected_point = plane.distance(target_point) + if( self.contains?(projected_point)) + return distance, projected_point + end + #check distance to FiniteLines + finite_lines = self.edges + return FiniteLine.ary_distanc_to_point(finite_lines, target_point) + end + + def distance_to_line(target_line) + plane = Plane.new( vertices[0], self.normal ) + distance, point_on_plane, parameter_on_line = plane.distance( target_line ) + if( point_on_plane == nil) + # parallel case + # check distance to FiniteLines + finite_lines = self.edges + distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target = + FiniteLine.ary_distance_to_line(finite_lines, target_line) + return distance, nil, nil, nil + end + if( self.contains?(point_on_plane) ) + return distance, point_on_plane, point_on_plane, parameter_on_line + end + # check distance to FiniteLines + finite_lines = self.edges + distance, point_on_edge, point_on_target, param_on_finiteline, param_on_target = + FiniteLine.ary_distance_to_line(finite_lines, target_line) + return distance, point_on_edge, point_on_target, param_on_target + end + + def distance_to_plane(target_plane) + triangle_plane = Plane.new( vertices[0], self.normal ) + distance, intersect_line_each_plane = triangle_plane.distance( target_plane ) + if( intersect_line_each_plane == nil ) + return distance, nil, nil, nil + end + + # check distance from intersection and each edge. + distance_zero_count = 0 + distance_info = Array.new(0) + prallel_edge_ary = Array.new(0) + self.edges.each do |edge| + distance, point_on_edge, point_on_line = edge.distance( intersect_line_each_plane) + if point_on_edge != nil && point_on_line != nil + distance_info.push([distance, point_on_edge, point_on_line]) + if distance <= self.tolerance + distance_zero_count += 1 + end + else + prallel_edge_ary.push( edge ) + end + end + distance_info.sort!{|a,b| a[0] <=> b[0]} + # distance, intersect_line(or closet edge), point_on_triangle, point_on_plan + if (distance_zero_count == 2) + point1 = distance_info[0][1] + point2 = distance_info[1][1] + if point1.distance(point2) > self.tolerance + return 0.0, FiniteLine.new(point1, point2), nil, nil + end + return 0.0, nil, point1, point1 + elsif (distance_zero_count == 0) + distance, closest_point_on_plane = target_plane.distance(distance_info[0][1]) + if(distance_info[0][1] != distance_info[1][1]) + return distance, FiniteLine.new(distance_info[0][1], distance_info[1][1]), nil, nil + end + return distance, nil, distance_info[0][1], closest_point_on_plane + end + return 0.0, nil, nil, nil + end + end +end