-
Notifications
You must be signed in to change notification settings - Fork 9
Axis Aligned Bounding Box
Martin Prout edited this page Aug 15, 2015
·
7 revisions
Currently this a convenience class and there may be no simple way of creating a jruby extension without giving up the keyword arguments (availability of required keyword args is what attracted me to create class in first place). There are two ways to create an instance of AABB:-
-
AABB.new(center: vec1, extent: vec2)
where vec1 and vec2 are 2D vectors ( use Vec2D for sanity, ducks allowed but may be incompetent ) -
AABB.from_min_max(min: vec1, max: vec2)
where vec1 and vec2 are 2D vectors ( use Vec2D for sanity, note ducks must support+
,-
and*
operations )
# Axis aligned bounding box
class AABB
attr_reader :center, :extent
def initialize(center:, extent:)
@center = center
@extent = extent
end
def self.from_min_max(min:, max:)
new(center: (min + max) * 0.5, extent: max - min)
end
def position(vec)
return @center = vec unless block_given?
@center = vec if yield
end
def scale(d)
@extent *= d
end
def contains?(vec)
rad = extent * 0.5
return false unless (center.x - rad.x..center.x + rad.x).cover? vec.x
(center.y - rad.y..center.y + rad.y).cover? vec.y
end
end
Here is some example usage:-
# Click on the box and drag it across the screen.
attr_reader :block, :block_locked, :over_block, :renderer, :bounds
BLOCK_WIDTH = 150
def setup
sketch_title 'AABB Example'
@block = Block.new(
center: Vec2D.new(width / 2, height / 2),
size: Vec2D.new(BLOCK_WIDTH, BLOCK_WIDTH))
@locked = false
@over_block = false
@bounds = AABB.new(
center: Vec2D.new(width / 2, height / 2),
extent: Vec2D.new(width - BLOCK_WIDTH, height - BLOCK_WIDTH))
@renderer = AppRender.new(self)
end
def draw
background 0
fill 153
if block.over?(Vec2D.new(mouse_x, mouse_y))
@over_block = true
stroke 255
fill 255 if block_locked?
else
@over_block = false
stroke 153
end
# Draw the box as a shape
begin_shape
block.points_array.each { |vec| vec.to_vertex(renderer) }
end_shape(CLOSE)
end
def block_locked?
block_locked
end
def over_block?
over_block
end
def mouse_pressed
if over_block?
@block_locked = true
fill 255
else
@block_locked = false
end
end
def mouse_dragged
return unless block_locked?
position = Vec2D.new(mouse_x, mouse_y)
block.new_position(position) { bounds.contains? position }
end
def mouse_released
@block_locked = false
end
def settings
size 640, 360
smooth 4
end
# Use class to contain block behaviour
class Block
attr_reader :aabb
def initialize(center:, size:)
@aabb = AABB.new(center: center, extent: size)
end
# passing ruby block on to @aabb.position
def new_position(center, &block)
@aabb.position(center.dup, &block)
end
def over?(vec)
aabb.contains? vec
end
# use for shape
def points_array
a = aabb.center - aabb.extent * 0.5
c = aabb.center + aabb.extent * 0.5
b = Vec2D.new(c.x, a.y)
d = Vec2D.new(a.x, c.y)
[a, b, c, d]
end
# use for rect
def points
[aabb.center, aabb.extent]
end
end
If you want efficiency (and extensibility) over awesomeness checkout toxicgem (hope I haven't created a name space clash oops?)