mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
datatypes: add quadtree, add its demo to examples/ (#16087)
This commit is contained in:
parent
cc9b754801
commit
e3379bca97
191
examples/quadtree_demo/quadtree_demo.v
Normal file
191
examples/quadtree_demo/quadtree_demo.v
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import datatypes
|
||||||
|
import gg
|
||||||
|
import gx
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import rand
|
||||||
|
|
||||||
|
const (
|
||||||
|
win_width = 1340
|
||||||
|
win_height = 640
|
||||||
|
timer_period = 40 * time.millisecond // defaulted at 25 fps
|
||||||
|
font_small = gx.TextCfg{
|
||||||
|
color: gx.black
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
font_large = gx.TextCfg{
|
||||||
|
color: gx.black
|
||||||
|
size: 40
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
mut:
|
||||||
|
gg &gg.Context
|
||||||
|
qt datatypes.Quadtree
|
||||||
|
players []datatypes.AABB
|
||||||
|
particles []Particle
|
||||||
|
retrieveds []datatypes.AABB
|
||||||
|
nodes []datatypes.Quadtree
|
||||||
|
width f64 = 1340
|
||||||
|
height f64 = 640
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Particle {
|
||||||
|
mut:
|
||||||
|
pmt datatypes.AABB
|
||||||
|
speed f64
|
||||||
|
angle f64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut p Particle) update() {
|
||||||
|
p.pmt.x += p.speed * math.cos(p.angle * math.pi / 180)
|
||||||
|
p.pmt.y += p.speed * math.sin(p.angle * math.pi / 180)
|
||||||
|
|
||||||
|
if p.pmt.x < 0 {
|
||||||
|
p.pmt.x = 0
|
||||||
|
p.speed = -p.speed
|
||||||
|
p.angle = -p.angle
|
||||||
|
}
|
||||||
|
if p.pmt.x > 1340 {
|
||||||
|
p.pmt.x = 1340
|
||||||
|
p.speed = -p.speed
|
||||||
|
p.angle = -p.angle
|
||||||
|
}
|
||||||
|
if p.pmt.y < 0 {
|
||||||
|
p.pmt.y = 0
|
||||||
|
p.speed = -p.speed
|
||||||
|
p.angle = 180 - p.angle
|
||||||
|
}
|
||||||
|
if p.pmt.y > 640 {
|
||||||
|
p.pmt.y = 640
|
||||||
|
p.speed = -p.speed
|
||||||
|
p.angle = 180 - p.angle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) start() {
|
||||||
|
app.players << datatypes.AABB{1200 * rand.f64(), 500 * rand.f64(), 20, 20}
|
||||||
|
app.insert_particles()
|
||||||
|
for mut particle in app.particles {
|
||||||
|
particle.speed = 10 * rand.f64()
|
||||||
|
particle.angle = 200 * rand.f64()
|
||||||
|
}
|
||||||
|
app.nodes << app.qt.get_nodes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) update() {
|
||||||
|
app.qt.clear()
|
||||||
|
app.nodes = []
|
||||||
|
for mut particle in app.particles {
|
||||||
|
particle.update()
|
||||||
|
app.qt.insert(particle.pmt)
|
||||||
|
}
|
||||||
|
app.find_particles()
|
||||||
|
app.nodes << app.qt.get_nodes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) insert_particles() {
|
||||||
|
mut grid := 10.0
|
||||||
|
mut gridh := app.qt.perimeter.width / grid
|
||||||
|
mut gridv := app.qt.perimeter.height / grid
|
||||||
|
num_particles := 100
|
||||||
|
for _ in 0 .. num_particles {
|
||||||
|
mut x := rand_minmax(0, gridh) * grid
|
||||||
|
mut y := rand_minmax(0, gridv) * grid
|
||||||
|
mut random_particle := datatypes.AABB{
|
||||||
|
x: x
|
||||||
|
y: y
|
||||||
|
width: rand_minmax(1, 4) * grid
|
||||||
|
height: rand_minmax(1, 4) * grid
|
||||||
|
}
|
||||||
|
app.particles << Particle{random_particle, 0.0, 0.0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) find_particles() {
|
||||||
|
app.retrieveds = []
|
||||||
|
app.retrieveds << app.qt.retrieve(app.players[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut app := &App{
|
||||||
|
gg: 0
|
||||||
|
}
|
||||||
|
app.gg = gg.new_context(
|
||||||
|
bg_color: gx.white
|
||||||
|
width: win_width
|
||||||
|
height: win_height
|
||||||
|
use_ortho: true
|
||||||
|
create_window: true
|
||||||
|
window_title: 'Quadtree Demo'
|
||||||
|
frame_fn: frame
|
||||||
|
event_fn: on_event
|
||||||
|
user_data: app
|
||||||
|
font_path: os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf')
|
||||||
|
)
|
||||||
|
app.qt = app.qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
app.start()
|
||||||
|
go app.run()
|
||||||
|
app.gg.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) on_mouse_move(mouse_x f32, mouse_y f32) {
|
||||||
|
for mut player in app.players {
|
||||||
|
player.x = (mouse_x / gg.window_size_real_pixels().width) * 1340
|
||||||
|
player.y = (mouse_y / gg.window_size_real_pixels().height) * 640
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(mut e gg.Event, mut app App) {
|
||||||
|
match e.typ {
|
||||||
|
.mouse_move { app.on_mouse_move(e.mouse_x, e.mouse_y) }
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) run() {
|
||||||
|
for {
|
||||||
|
app.update()
|
||||||
|
time.sleep(timer_period)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(app &App) {
|
||||||
|
app.gg.begin()
|
||||||
|
app.draw()
|
||||||
|
app.gg.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (app &App) display() {
|
||||||
|
for player in app.players {
|
||||||
|
app.gg.draw_rect_filled(f32(player.x), f32(player.y), f32(player.width), f32(player.height),
|
||||||
|
gx.black)
|
||||||
|
}
|
||||||
|
for particle in app.particles {
|
||||||
|
app.gg.draw_rect_empty(f32(particle.pmt.x), f32(particle.pmt.y), f32(particle.pmt.width),
|
||||||
|
f32(particle.pmt.height), gx.blue)
|
||||||
|
}
|
||||||
|
for node in app.nodes {
|
||||||
|
app.gg.draw_rect_empty(f32(node.perimeter.x), f32(node.perimeter.y), f32(node.perimeter.width),
|
||||||
|
f32(node.perimeter.height), gx.red)
|
||||||
|
}
|
||||||
|
for retrieved in app.retrieveds {
|
||||||
|
app.gg.draw_rect_filled(f32(retrieved.x + 1), f32(retrieved.y + 1), f32(retrieved.width - 2),
|
||||||
|
f32(retrieved.height - 2), gx.green)
|
||||||
|
}
|
||||||
|
app.gg.draw_text(1200, 25, 'Nodes: $app.nodes.len', font_small)
|
||||||
|
app.gg.draw_text(1200, 50, 'Particles: $app.particles.len', font_small)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (app &App) draw() {
|
||||||
|
app.display()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rand_minmax(min f64, max f64) f64 {
|
||||||
|
mut val := min + (rand.f64() * (max - min))
|
||||||
|
return val
|
||||||
|
}
|
@ -27,4 +27,5 @@ println(stack)
|
|||||||
- [x] Queue (FIFO)
|
- [x] Queue (FIFO)
|
||||||
- [x] Min heap (priority queue)
|
- [x] Min heap (priority queue)
|
||||||
- [x] Set
|
- [x] Set
|
||||||
|
- [x] Quadtree
|
||||||
- [ ] ...
|
- [ ] ...
|
||||||
|
205
vlib/datatypes/quadtree.v
Normal file
205
vlib/datatypes/quadtree.v
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
module datatypes
|
||||||
|
|
||||||
|
pub struct AABB {
|
||||||
|
pub mut:
|
||||||
|
x f64
|
||||||
|
y f64
|
||||||
|
width f64
|
||||||
|
height f64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Quadtree {
|
||||||
|
pub mut:
|
||||||
|
perimeter AABB
|
||||||
|
capacity int
|
||||||
|
depth int
|
||||||
|
level int
|
||||||
|
particles []AABB
|
||||||
|
nodes []Quadtree
|
||||||
|
}
|
||||||
|
|
||||||
|
// create returns a new configurable root node for the tree.
|
||||||
|
pub fn (mut q Quadtree) create(x f64, y f64, width f64, height f64, capacity int, depth int, level int) Quadtree {
|
||||||
|
return Quadtree{
|
||||||
|
perimeter: AABB{
|
||||||
|
x: x
|
||||||
|
y: y
|
||||||
|
width: width
|
||||||
|
height: height
|
||||||
|
}
|
||||||
|
capacity: capacity
|
||||||
|
depth: depth
|
||||||
|
level: level
|
||||||
|
particles: []AABB{}
|
||||||
|
nodes: []Quadtree{len: 0, cap: 4}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert recursevely adds a particle in the correct index of the tree.
|
||||||
|
pub fn (mut q Quadtree) insert(p AABB) {
|
||||||
|
mut indexes := []int{}
|
||||||
|
|
||||||
|
if q.nodes.len > 0 {
|
||||||
|
indexes = q.get_index(p)
|
||||||
|
for k in 0 .. indexes.len {
|
||||||
|
q.nodes[indexes[k]].insert(p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q.particles << p
|
||||||
|
|
||||||
|
if (q.particles.len > q.capacity) && (q.level < q.depth) {
|
||||||
|
if q.nodes.len == 0 {
|
||||||
|
q.split()
|
||||||
|
}
|
||||||
|
|
||||||
|
for j in 0 .. q.particles.len {
|
||||||
|
indexes = q.get_index(q.particles[j])
|
||||||
|
for k in 0 .. indexes.len {
|
||||||
|
q.nodes[indexes[k]].insert(q.particles[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.particles = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve recursevely checks if a particle is in a specific index of the tree.
|
||||||
|
pub fn (mut q Quadtree) retrieve(p AABB) []AABB {
|
||||||
|
mut indexes := q.get_index(p)
|
||||||
|
mut detected_particles := q.particles.clone()
|
||||||
|
|
||||||
|
if q.nodes.len > 0 {
|
||||||
|
for j in 0 .. indexes.len {
|
||||||
|
detected_particles << q.nodes[indexes[j]].retrieve(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return detected_particles
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear flushes out nodes and partcles from the tree.
|
||||||
|
pub fn (mut q Quadtree) clear() {
|
||||||
|
q.particles = []
|
||||||
|
for j in 0 .. q.nodes.len {
|
||||||
|
if q.nodes.len > 0 {
|
||||||
|
q.nodes[j].clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.nodes = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// get_nodes recursevely returns the subdivisions the tree has.
|
||||||
|
pub fn (q Quadtree) get_nodes() []Quadtree {
|
||||||
|
mut nodes := []Quadtree{}
|
||||||
|
if q.nodes.len > 0 {
|
||||||
|
for j in 0 .. q.nodes.len {
|
||||||
|
nodes << q.nodes[j]
|
||||||
|
nodes << q.nodes[j].get_nodes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut q Quadtree) split() {
|
||||||
|
if q.nodes.len == 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next_level := q.level + 1
|
||||||
|
child_width := q.perimeter.width / 2
|
||||||
|
child_height := q.perimeter.height / 2
|
||||||
|
x := q.perimeter.x
|
||||||
|
y := q.perimeter.y
|
||||||
|
|
||||||
|
//(0)
|
||||||
|
q.nodes << Quadtree{
|
||||||
|
perimeter: AABB{
|
||||||
|
x: x + child_width
|
||||||
|
y: y
|
||||||
|
width: child_width
|
||||||
|
height: child_height
|
||||||
|
}
|
||||||
|
capacity: q.capacity
|
||||||
|
depth: q.depth
|
||||||
|
level: next_level
|
||||||
|
particles: []AABB{}
|
||||||
|
nodes: []Quadtree{len: 0, cap: 4}
|
||||||
|
}
|
||||||
|
|
||||||
|
//(1)
|
||||||
|
q.nodes << Quadtree{
|
||||||
|
perimeter: AABB{
|
||||||
|
x: x
|
||||||
|
y: y
|
||||||
|
width: child_width
|
||||||
|
height: child_height
|
||||||
|
}
|
||||||
|
capacity: q.capacity
|
||||||
|
depth: q.depth
|
||||||
|
level: next_level
|
||||||
|
particles: []AABB{}
|
||||||
|
nodes: []Quadtree{len: 0, cap: 4}
|
||||||
|
}
|
||||||
|
|
||||||
|
//(2)
|
||||||
|
q.nodes << Quadtree{
|
||||||
|
perimeter: AABB{
|
||||||
|
x: x
|
||||||
|
y: y + child_height
|
||||||
|
width: child_width
|
||||||
|
height: child_height
|
||||||
|
}
|
||||||
|
capacity: q.capacity
|
||||||
|
depth: q.depth
|
||||||
|
level: next_level
|
||||||
|
particles: []AABB{}
|
||||||
|
nodes: []Quadtree{len: 0, cap: 4}
|
||||||
|
}
|
||||||
|
|
||||||
|
//(3)
|
||||||
|
q.nodes << Quadtree{
|
||||||
|
perimeter: AABB{
|
||||||
|
x: x + child_width
|
||||||
|
y: y + child_height
|
||||||
|
width: child_width
|
||||||
|
height: child_height
|
||||||
|
}
|
||||||
|
capacity: q.capacity
|
||||||
|
depth: q.depth
|
||||||
|
level: next_level
|
||||||
|
particles: []AABB{}
|
||||||
|
nodes: []Quadtree{len: 0, cap: 4}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut q Quadtree) get_index(p AABB) []int {
|
||||||
|
mut indexes := []int{}
|
||||||
|
mut v_midpoint := q.perimeter.x + (q.perimeter.width / 2)
|
||||||
|
mut h_midpoint := q.perimeter.y + (q.perimeter.height / 2)
|
||||||
|
|
||||||
|
mut north := p.y < h_midpoint
|
||||||
|
mut south := p.y + p.height > h_midpoint
|
||||||
|
mut west := p.x < v_midpoint
|
||||||
|
mut east := p.x + p.width > v_midpoint
|
||||||
|
|
||||||
|
// top-right quad
|
||||||
|
if north && east {
|
||||||
|
indexes << 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// top-left quad
|
||||||
|
if north && west {
|
||||||
|
indexes << 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom-left quad
|
||||||
|
if south && west {
|
||||||
|
indexes << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom-right quad
|
||||||
|
if south && east {
|
||||||
|
indexes << 3
|
||||||
|
}
|
||||||
|
return indexes
|
||||||
|
}
|
83
vlib/datatypes/quadtree_test.v
Normal file
83
vlib/datatypes/quadtree_test.v
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
module datatypes
|
||||||
|
|
||||||
|
fn test_create() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
test_clone := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
assert test == test_clone
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_insert() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
mut test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
mut pt := AABB{
|
||||||
|
x: 100
|
||||||
|
y: 50
|
||||||
|
width: 60
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
assert test.particles == []
|
||||||
|
test.insert(pt)
|
||||||
|
assert test.particles[0] == pt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_retrieve() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
mut test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
mut pt := AABB{
|
||||||
|
x: 100
|
||||||
|
y: 50
|
||||||
|
width: 60
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
test.insert(pt)
|
||||||
|
t := test.retrieve(pt)
|
||||||
|
assert t[0] == pt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_clear() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
mut test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
mut test_clone := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
mut pt := AABB{
|
||||||
|
x: 100
|
||||||
|
y: 50
|
||||||
|
width: 60
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
test.split()
|
||||||
|
test.insert(pt)
|
||||||
|
assert test != test_clone
|
||||||
|
test.clear()
|
||||||
|
assert test == test_clone
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_get_nodes() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
mut test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
test.split()
|
||||||
|
t := test.get_nodes()
|
||||||
|
assert t.len == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_split() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
mut test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
test.split()
|
||||||
|
t := test.get_nodes()
|
||||||
|
assert t.len == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_get_index() {
|
||||||
|
mut qt := Quadtree{}
|
||||||
|
mut test := qt.create(0, 0, 1340, 640, 8, 4, 0)
|
||||||
|
mut pt := AABB{
|
||||||
|
x: 100
|
||||||
|
y: 50
|
||||||
|
width: 60
|
||||||
|
height: 100
|
||||||
|
}
|
||||||
|
test.particles << pt
|
||||||
|
t := test.get_index(pt)
|
||||||
|
assert t == [1]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user