Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Created June 3, 2022 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save amirrajan/d949daaeda2c3b83f3dcdb90e6b731da to your computer and use it in GitHub Desktop.
Save amirrajan/d949daaeda2c3b83f3dcdb90e6b731da to your computer and use it in GitHub Desktop.
Quad tree implemented in Ruby
class Hash
def x
self[:x]
end
def y
self[:y]
end
def w
self[:w]
end
def h
self[:h]
end
def bounding_box
self[:bounding_box]
end
def bottom_left
self[:bottom_left]
end
def bottom_left= value
self[:bottom_left] = value
end
def bottom_right
self[:bottom_right]
end
def bottom_right= value
self[:bottom_right] = value
end
def top_left
self[:top_left]
end
def top_left= value
self[:top_left] = value
end
def top_right
self[:top_right]
end
def top_right= value
self[:top_right] = value
end
def rects
self[:rects]
end
end
class QuadTree
class << self
def intersect_rect? rect_one, rect_two
return false if rect_two.x + rect_two.w < rect_one.x
return false if rect_two.y + rect_two.y < rect_one.y
return false if rect_two.x > rect_one.x + rect_one.w
return false if rect_two.y > rect_one.y + rect_one.h
return true
end
def inside_rect? outer, inner
(inner.x) >= (outer.x) &&
(inner.x + inner.w) <= (outer.x + outer.w) &&
(inner.y) >= (outer.y) &&
(inner.y + inner.h) <= (outer.y + outer.h)
end
def __bounding_box__ rects
return { x: 0, y: 0, w: 0, h: 0 } if !rects || rects.length == 0
min_x = rects.first.x
min_y = rects.first.y
max_x = rects.first.x + rects.first.w
max_y = rects.first.y + rects.first.h
rects.each do |r|
min_x = r.x if r.x < min_x
min_y = r.y if r.y < min_y
max_x = r.x + r.w if (r.x + r.w) > max_x
max_y = r.y + r.w if (r.y + r.w) > max_y
end
{ x: min_x, y: min_y, w: max_x - min_x, h: max_y - min_y }
end
def __insert_rect__ node, rect
return if !inside_rect? node.bounding_box, rect
node.top_left ||= {
bounding_box: { x: node.bounding_box.x,
y: node.bounding_box.y + node.bounding_box.h / 2,
w: node.bounding_box.w / 2,
h: node.bounding_box.h / 2 },
rects: []
}
node.top_right ||= {
bounding_box: { x: node.bounding_box.x + node.bounding_box.w / 2,
y: node.bounding_box.y + node.bounding_box.h / 2,
w: node.bounding_box.w / 2,
h: node.bounding_box.h / 2 },
rects: []
}
node.bottom_left ||= {
bounding_box: { x: node.bounding_box.x,
y: node.bounding_box.y,
w: node.bounding_box.w / 2,
h: node.bounding_box.h / 2 },
rects: []
}
node.bottom_right ||= {
bounding_box: { x: node.bounding_box.x + node.bounding_box.w / 2,
y: node.bounding_box.y,
w: node.bounding_box.w / 2,
h: node.bounding_box.h / 2 },
rects: []
}
if inside_rect? node.top_left.bounding_box, rect
__insert_rect__ node.top_left, rect
elsif inside_rect? node.top_right.bounding_box, rect
__insert_rect__ node.top_right, rect
elsif inside_rect? node.bottom_left.bounding_box, rect
__insert_rect__ node.bottom_left, rect
elsif inside_rect? node.bottom_right.bounding_box, rect
__insert_rect__ node.bottom_right, rect
else
node.rects << rect
end
end
def create rects
tree = {
bounding_box: (__bounding_box__ rects),
rects: []
}
rects.each { |rect| __insert_rect__ tree, rect }
tree
end
def find_intersect node, rect
return nil if !intersect_rect? node.bounding_box, rect
result = node.rects.find { |r| intersect_rect? r, rect }
if !result && intersect_rect?(node.top_left.bounding_box, rect)
result = find_intersect node.top_left, rect
end
if !result && intersect_rect?(node.top_right.bounding_box, rect)
result = find_intersect node.top_right, rect
end
if !result && intersect_rect?(node.bottom_left.bounding_box, rect)
result = find_intersect node.bottom_left, rect
end
if !result && intersect_rect?(node.bottom_right.bounding_box, rect)
result = find_intersect node.bottom_right, rect
end
result
end
end
end
tree = QuadTree.create([
{ x: 0, y: 0, w: 100, h: 100 },
{ x: 0, y: 0, w: 200, h: 200 },
{ x: 200, y: 200, w: 200, h: 200 },
])
result = QuadTree.find_intersect tree, { x: 300, y: 300, w: 10, h: 10 }
puts result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment