Created
June 3, 2022 15:39
-
-
Save amirrajan/d949daaeda2c3b83f3dcdb90e6b731da to your computer and use it in GitHub Desktop.
Quad tree implemented in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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