Commit f9f55b39 authored by Sam Rushing's avatar Sam Rushing

separate quadtrees for background, viewports, moving objects

use cython quadtree implementation
parent f50c1993
# -*- Mode: Python -*-
from coro.http.websocket import handler, websocket
import math
import pickle
import random
import re
import region
import quadtree
import coro
W = coro.write_stderr
# need a way to declare a dirty region, and send a single
# redraw command for all the dirtied objects. So we need to separate
# object movement from redrawing... probably with a timer of some kind,
# accumulate dirty rects, then redraw in one swell foop
# another thing to consider: sending deltas rather than the entire list.
# for example, if the viewport hasn't moved, then the list of rectangles
# won't be changing. Can we send a diff? [this might just be easier with layers]
# for layers see: http://stackoverflow.com/questions/3008635/html5-canvas-element-multiple-layers
colors = ['red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange']
# sample 'box' object.
class box:
def __init__ (self, color, rect):
class box (quadtree.ob):
def __init__ (self, color, (l,t,r,b)):
self.color = color
self.rect = rect
self.set_rect (l,t,r,b)
def move (self, dx, dy):
x0, y0, x1, y1 = self.get_rect()
self.set_rect (
int(x0 + dx), int(y0 + dy), int(x1 + dx), int(y0 + dy)
)
def cmd (self, xoff, yoff):
# command to draw me relative to <xoff,yoff>?
x0, y0, x1, y1 = self.get_rect()
return 'B,%s,%d,%d,%d,%d' % (self.color, x0-xoff, y0-yoff, x1-x0, y1-y0)
def __repr__ (self):
return '<box (%d,%d,%d,%d)>' % self.get_rect()
class circle (box):
def __init__ (self, color, center, radius):
x, y = center
r = radius
self.color = color
self.center = center
self.radius = radius
self.set_rect (*self.get_rect())
def get_rect (self):
return self.rect
x, y = self.center
r = self.radius
return x-r, y-r, x+r, y+r
def move (self, dx, dy):
x, y = self.center
self.center = int(x + dx), int(y + dy)
self.set_rect (*self.get_rect())
def cmd (self, xoff, yoff):
# command to draw me relative to <xoff,yoff>?
x, y = self.center
return 'C,%s,%d,%d,%d' % (self.color, x-xoff, y-yoff, self.radius)
def __repr__ (self):
return '<box (%d,%d,%d,%d)>' % self.rect
return '<circle (%d,%d) radius=%d)>' % (self.center[0], self.center[1], self.radius)
# should have multiple qt's:
# 1) for the background, immutable
# 2) for the client viewports
# 3) for moving objects
class field:
def __init__ (self, w=1024*20, h=1024*20):
self.w = w
self.h = h
self.clients = set()
self.Q = quadtree.quadtree()
self.Q_views = quadtree.quadtree()
self.Q_back = quadtree.quadtree()
self.Q_obs = quadtree.quadtree()
self.generate_random_field()
def generate_random_field (self):
for i in range (5000):
c = random.choice (colors)
x = random.randint (0, self.w - 100)
y = random.randint (0, self.h - 100)
w = random.randint (50, 300)
h = random.randint (50, 300)
c = random.choice (colors)
b = box (c, (x, y, x+w, y+h))
self.Q.insert (b)
self.Q_back.insert (b)
for i in range (1000):
coro.spawn (self.wanderer)
def new_conn (self, *args, **kwargs):
c = field_conn (self, *args, **kwargs)
self.clients.add (c)
self.Q_views.insert (c)
def new_ob (self, ob):
self.Q_obs.insert (ob)
#W ('new ob %r\n' % (self.Q_obs,))
#self.Q_obs.dump()
def move_ob (self, ob, dx, dy):
r0 = ob.get_rect()
self.Q_obs.delete (ob)
ob.move (dx, dy)
r1 = ob.get_rect()
self.Q_obs.insert (ob)
#self.Q_obs.dump()
# notify any viewers
r2 = region.union (r0, r1)
for client in self.Q_views.search (r2):
client.draw_window()
class field_conn (websocket):
sleep = 0.1
def wanderer (self):
# spawn me!
x = random.randint (100, self.w - 100)
y = random.randint (100, self.h - 100)
c = random.choice (colors)
ob = circle (c, (x, y), 25)
self.new_ob (ob)
while 1:
# pick a random direction
heading = random.randint (0, 360) * (math.pi / 180.0)
speed = (random.random() * 10) + 3
dx = math.cos (heading) * speed
dy = math.sin (heading) * speed
# go that way for 20 steps
for i in range (20):
self.move_ob (ob, dx, dy)
# not working yet...
## if not ob.range_check (0, 0, self.w, self.h):
## W ('%r hit an edge!\n' % (ob,))
## dx = - (dx * 5)
## dy = - (dy * 5)
## self.move_ob (ob, dx, dy)
coro.sleep_relative (self.sleep)
class field_conn (websocket, quadtree.ob):
def __init__ (self, field, *args, **kwargs):
websocket.__init__ (self, *args, **kwargs)
......@@ -52,12 +144,13 @@ class field_conn (websocket):
self.field = field
rx = random.randint (0, self.field.w-1024)
ry = random.randint (0, self.field.h-1024)
self.rect = (rx, ry, rx+1024, ry+1024)
self.set_rect (rx, ry, rx+1024, ry+1024)
self.draw_window()
self.mouse_down = None, None
def move_window (self, mx, my):
x0, y0, x1, y1 = self.rect
self.field.Q_views.delete (self)
x0, y0, x1, y1 = self.get_rect()
x0 = x0 + mx; y0 = y0 + my
x1 = x1 + mx; y1 = y1 + my
if x0 < 0:
......@@ -68,17 +161,20 @@ class field_conn (websocket):
x1 = self.field.w-1024; x0 = x1 - 1024
if y1 > self.field.h-1024:
y1 = self.field.h-1024; y0 = y1 - 1024
self.rect = (x0, y0, x1, y1)
self.set_rect (x0, y0, x1, y1)
self.field.Q_views.insert (self)
self.draw_window()
self.send_text ('M pos=%d,%d' % self.rect[:2])
self.send_text ('M pos=%d,%d' % self.get_rect()[:2])
def draw_qt (self, r, Q):
px, py = self.get_rect()[:2]
for ob in Q.search (self.rect):
r.append (ob.cmd (px, py))
def draw_window (self):
r = ['F']
px, py = self.rect[:2]
for ob in self.field.Q.search_gen (self.rect):
c = ob.color
x0, y0, x1, y1 = ob.get_rect()
r.append ('B,%s,%d,%d,%d,%d' % (c, x0-px, y0-py, x1-x0, y1-y0))
self.draw_qt (r, self.field.Q_back)
self.draw_qt (r, self.field.Q_obs)
self.send_text ('|'.join (r))
def send_text (self, payload):
......@@ -100,7 +196,7 @@ class field_conn (websocket):
elif ascii == 68: # D
self.move_window (10, 0)
elif ascii == 82: # R
x0, y0 = self.rect[:2]
x0, y0 = self.get_rect()[:2]
self.move_window (-x0, -y0)
elif event[0] == 'MD':
self.on_mouse_down (int (event[1]), int (event[2]))
......@@ -135,15 +231,6 @@ class field_conn (websocket):
def on_mouse_up (self, x1, y1):
x0, y0 = self.mouse_down
self.mouse_down = None, None
# 1) draw a box in the region chosen
#if x0 > x1:
# x0, x1 = x1, x0
#if y0 > y1:
# y0, y1 = y1, y0
#px, py = self.rect[:2]
#b = box (random.choice (colors), (x0+px, y0+py, x1+px, y1+py))
#self.field.Q.insert (b)
# 2) or move the window
if x0 is not None:
self.move_window (x0-x1, y0-y1)
self.draw_window()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment