Skip to content

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).

# 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
Clone this wiki locally