207 lines
6.6 KiB
Ruby
Executable File
207 lines
6.6 KiB
Ruby
Executable File
#
|
|
# File:: bbox.rb
|
|
# Description:: Bounding Box Class
|
|
#
|
|
# Author:: David Muir <david.muir@rockstarnorth.com>
|
|
# Date:: 28 February 2008
|
|
#
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Uses
|
|
#-----------------------------------------------------------------------------
|
|
require 'pipeline/math/vector2'
|
|
require 'pipeline/math/vector3'
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Implementation
|
|
#-----------------------------------------------------------------------------
|
|
module Pipeline
|
|
module Math
|
|
|
|
#
|
|
# == Description
|
|
# Class representing a 3D Bounding Box.
|
|
#
|
|
class BoundingBox3
|
|
class Invalid < Exception; end
|
|
|
|
XML_TAG = 'boundingbox3'
|
|
|
|
attr_accessor :min
|
|
attr_accessor :max
|
|
|
|
def initialize( min = nil, max = nil )
|
|
throw Invalid.new( "BoundingBox3 min must be Vector3 type (#{min.class})." ) \
|
|
unless ( min.nil? or min.is_a?( Vector3 ) )
|
|
throw Invalid.new( "BoundingBox3 max must be Vector3 type (#{max.class}).") \
|
|
unless ( max.nil? or max.is_a?( Vector3 ) )
|
|
|
|
@min = Vector3.new( Float::MAX, Float::MAX, Float::MAX ) if ( min.nil? )
|
|
@min = min unless ( min.nil? )
|
|
@max = Vector3.new( Float::MIN, Float::MIN, Float::MIN ) if ( max.nil? )
|
|
@max = max unless ( max.nil? )
|
|
end
|
|
|
|
#
|
|
# Return true if vector p is within the bounding box, false otherwise.
|
|
#
|
|
def contains( p )
|
|
raise ArgumentError.new( "p must be a Vector3 or BoundingBox3 object" ) \
|
|
unless ( p.is_a?( Vector3 ) or ( p.is_a?( BoundingBox3 ) ) )
|
|
|
|
# Vector3 Test
|
|
return (( p.x >= @min.x ) and ( p.y >= @min.y ) and ( p.z >= @min.z ) and
|
|
( p.x <= @max.x ) and ( p.y <= @max.y ) and ( p.z <= @max.z )) if ( p.is_a?( Vector3 ) )
|
|
# BoundingBox Test
|
|
return ( ( p.min >= self.min ) and ( p.max <= self.max ) ) if ( p.is_a?( BoundingBox3 ) )
|
|
end
|
|
|
|
#
|
|
# Expand the bounding box to include the specified Vector3 or BoundingBox3.
|
|
#
|
|
def expand( v )
|
|
raise ArgumentError.new( "v must be a Vector3 or BoundingBox3 object (#{v.class})." ) \
|
|
unless ( v.is_a?( Vector3 ) or v.is_a?( BoundingBox3 ) )
|
|
|
|
if ( v.is_a?( Vector3 ) ) then
|
|
@min.x = v.x if v.x < @min.x
|
|
@min.y = v.y if v.y < @min.y
|
|
@min.z = v.z if v.z < @min.z
|
|
@max.x = v.x if v.x > @max.x
|
|
@max.y = v.y if v.y > @max.y
|
|
@max.z = v.z if v.z > @max.z
|
|
|
|
elsif ( v.is_a?( BoundingBox3 ) ) then
|
|
expand( v.min )
|
|
expand( v.max )
|
|
end
|
|
end
|
|
|
|
#
|
|
# Alias for contains.
|
|
#
|
|
alias include? contains
|
|
|
|
#--------------------------------------------------------------------
|
|
# Class Methods
|
|
#--------------------------------------------------------------------
|
|
|
|
#
|
|
# Used by the SceneXml module in pipeline/fileformats, to read the
|
|
# bounding box representation from an XML file.
|
|
#
|
|
def BoundingBox3::from_xml( xml_node )
|
|
raise ArgumentError.new( "Invalid BoundingBox3 node, incorrect name: #{xml_node.name}." ) \
|
|
unless ( XML_TAG == xml_node.name )
|
|
|
|
min = nil
|
|
max = nil
|
|
if ( xml_node.is_a?( LibXML::XML::Node ) ) then
|
|
|
|
min = Vector3::from_xml( xml_node.find_first( 'min' ) )
|
|
max = Vector3::from_xml( xml_node.find_first( 'max' ) )
|
|
|
|
elsif ( xml_node.is_a?( REXML::Node ) ) then
|
|
|
|
min = Vector3::from_xml( xml_node.elements['min'] )
|
|
max = Vector3::from_xml( xml_node.elements['max'] )
|
|
end
|
|
BoundingBox3::new( min, max )
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# Class representing a 2D Bounding Box.
|
|
#
|
|
class BoundingBox2
|
|
class Invalid < Exception; end
|
|
|
|
XML_TAG = 'boundingbox2'
|
|
|
|
attr_accessor :min
|
|
attr_accessor :max
|
|
|
|
def initialize( min = nil, max = nil )
|
|
throw Invalid.new( "BoundingBox2 min must be Vector2 type (#{min.class})." ) \
|
|
unless ( min.nil? or min.is_a?( Vector2 ) )
|
|
throw Invalid.new( "BoundingBox2 max must be Vector2 type (#{max.class}).") \
|
|
unless ( max.nil? or max.is_a?( Vector2 ) )
|
|
|
|
@min = Vector2.new( Float::MAX, Float::MAX ) if ( min.nil? )
|
|
@min = min unless ( min.nil? )
|
|
@max = Vector2.new( Float::MIN, Float::MIN ) if ( max.nil? )
|
|
@max = max unless ( max.nil? )
|
|
end
|
|
|
|
#
|
|
# Return true if vector p is within the bounding box, false otherwise.
|
|
#
|
|
def contains( p )
|
|
raise ArgumentError.new( "p must be a Vector2 or BoundingBox2 object" ) \
|
|
unless ( p.is_a?( Vector2 ) or ( p.is_a?( BoundingBox2 ) ) )
|
|
|
|
# Vector3 Test
|
|
return ( ( p.x >= @min.x ) and ( p.y >= @min.y ) and
|
|
( p.x <= @max.x ) and ( p.y <= @max.y ) ) if ( p.is_a?( Vector3 ) )
|
|
# BoundingBox2 Test
|
|
return ( ( p.min >= self.min ) and ( p.max <= self.max ) ) if ( p.is_a?( BoundingBox2 ) )
|
|
end
|
|
|
|
#
|
|
# Expand the bounding box to include the specified Vector2 or BoundingBox2.
|
|
#
|
|
def expand( v )
|
|
raise ArgumentError.new( "v must be a Vector2 or BoundingBox2 object (#{v.class})." ) \
|
|
unless ( v.is_a?( Vector2 ) or v.is_a?( BoundingBox2 ) )
|
|
|
|
if ( v.is_a?( Vector3 ) ) then
|
|
@min.x = v.x if v.x < @min.x
|
|
@min.y = v.y if v.y < @min.y
|
|
@max.x = v.x if v.x > @max.x
|
|
@max.y = v.y if v.y > @max.y
|
|
|
|
elsif ( v.is_a?( BoundingBox2 ) ) then
|
|
expand( v.min )
|
|
expand( v.max )
|
|
end
|
|
end
|
|
|
|
#
|
|
# Alias for contains.
|
|
#
|
|
alias include? contains
|
|
|
|
#--------------------------------------------------------------------
|
|
# Class Methods
|
|
#--------------------------------------------------------------------
|
|
|
|
#
|
|
# Used by the SceneXml module in pipeline/fileformats, to read the
|
|
# bounding box representation from an XML file.
|
|
#
|
|
def BoundingBox2::from_xml( xml_node )
|
|
raise ArgumentError.new( "Invalid boundingbox2 node, incorrect name: #{xml_node.name}." ) \
|
|
unless ( XML_TAG == xml_node.name )
|
|
|
|
min = nil
|
|
max = nil
|
|
if ( xml_node.is_a?( LibXML::XML::Node ) ) then
|
|
|
|
min = Vector2::from_xml( xml_node.find_first( 'min' ) )
|
|
max = Vector2::from_xml( xml_node.find_first( 'max' ) )
|
|
|
|
elsif ( xml_node.is_a?( REXML::Node ) ) then
|
|
|
|
min = Vector2::from_xml( xml_node.elements['min'] )
|
|
max = Vector2::from_xml( xml_node.elements['max'] )
|
|
end
|
|
BoundingBox2::new( min, max )
|
|
end
|
|
end
|
|
|
|
end # Math module
|
|
end # Pipeline module
|
|
|
|
# End of bbox.rb
|