class TestKDMethods(unittest.TestCase): def setUp(self): self.kd = KDTree() def tearDown(self): self.kd = None def test_basic(self): self.assertTrue(self.kd.add((10, 20))) self.assertFalse(self.kd.add((10, 20))) self.assertTrue(self.kd.find((10,20))) self.assertFalse(self.kd.find((5, 5))) def expand(self, region): """When Full Sub-trees returned as nodes traverse to expand nodes.""" result = [p for p in self.kd.range(region)] expanded = [] for pair in result: if pair[1]: expanded = expanded + [d.point for d in pair[0].inorder()] else: expanded.append(pair[0].point) return expanded def test_adding(self): for _ in range(5000): self.kd.add((random.randint(250,750), random.randint(250,750))) # make sure not to overlap! q_ne = self.expand(Region(500, 500, 1000, 1000)) q_nw = self.expand(Region(0, 500, 499, 1000)) q_sw = self.expand(Region(0, 0, 499, 499)) q_se = self.expand(Region(500, 0, 1000, 499)) q_all = self.expand(Region(0,0, 1000, 1000)) # quick check to see if any were missing combined = q_ne + q_nw + q_sw + q_se for q in q_all: if q not in combined: print (q, " missing ") self.assertEquals(len(q_all), len(combined)) # validate searches are true for p in combined: self.assertTrue(self.kd.find(p)) # validate can't add these points anymore for p in combined: self.assertFalse(self.kd.add(p))
class KDTreeApp: def __init__(self): """App for creating KD tree dynamically and executing nearest neighbor queries.""" self.tree = KDTree() self.match = None self.shortline = None # set to true when you want to view a set of points that # were set not by user control. self.static = False self.master = tkinter.Tk() self.master.title('KD Tree Nearest Neighbor Application') self.w = tkinter.Frame(self.master, width=410, height=410) self.canvas = tkinter.Canvas(self.w, width=400, height=400) self.paint() self.canvas.bind("<Button-1>", self.click) self.canvas.bind("<Motion>", self.moved) self.w.pack() def toCartesian(self, y): """Convert tkinter point into Cartesian.""" return self.w.winfo_height() - y def toTk(self, y): """Convert Cartesian into tkinter point.""" if y == maxValue: return 0 tk_y = self.w.winfo_height() if y != minValue: tk_y -= y return tk_y def moved(self, event): """React to mouse move events.""" if self.static: self.paint() return p = (event.x, self.toCartesian(event.y)) match = self.tree.find(p) if match: p = match.point self.canvas.create_rectangle(p[X] - RectangleSize, self.toTk(p[Y]) - RectangleSize, p[X] + RectangleSize, self.toTk(p[Y]) + RectangleSize, fill='Red', tags=closest) self.canvas.delete(self.shortline) self.shortline = None else: self.canvas.delete(closest) n = self.tree.nearest(p) if n: pn = n.point if self.shortline is None: self.shortline = self.canvas.create_line(pn[X], self.toTk(pn[Y]), p[X], self.toTk(p[Y]), fill='Red', dash=(2, 4), tags=shortline) else: self.canvas.coords(shortline, pn[X], self.toTk(pn[Y]), p[X], self.toTk(p[Y])) def click(self, event): """Add point to KDTree.""" p = (event.x, self.toCartesian(event.y)) self.tree.add(p) if self.shortline: self.canvas.delete(self.shortline) self.shortline = None self.paint() def drawPartition(self, r, p, orient): """Draw proper partition for given node (r,p) and orientation.""" if orient == VERTICAL: self.canvas.create_line(p[X], self.toTk(r.y_min), p[X], self.toTk(r.y_max)) else: xlow = r.x_min if r.x_min <= minValue: xlow = 0 xhigh = r.x_max if r.x_max >= maxValue: xhigh = self.w.winfo_width() self.canvas.create_line(xlow, self.toTk(p[Y]), xhigh, self.toTk(p[Y])) self.canvas.create_rectangle(p[X] - RectangleSize, self.toTk(p[Y]) - RectangleSize, p[X] + RectangleSize, self.toTk(p[Y]) + RectangleSize, fill='Black') def visit(self, n): """Draw KDNode properly partitioned and its sub-children.""" if n == None: return self.drawPartition(n.region, n.point, n.orient) self.visit(n.below) self.visit(n.above) def prepare(self, event): """prepare to add points.""" if self.label: self.label.destroy() self.label = None self.canvas.pack() def paint(self): """Paint KDTree by visiting all nodes, or show introductory message.""" if self.tree.root: self.canvas.delete(tkinter.ALL) self.visit(self.tree.root) else: self.label = tkinter.Label(self.w, width=100, height=40, text="Click To Add Points") self.label.bind("<Button-1>", self.prepare) self.label.pack()
class KDTreeApp: def __init__(self): """App for creating KD tree dynamically and executing nearest neighbor queries.""" self.tree = KDTree() self.match = None self.shortline = None # set to true when you want to view a set of points that # were set not by user control. self.static = False self.master = tkinter.Tk() self.master.title('KD Tree Nearest Neighbor Application') self.w = tkinter.Frame(self.master, width=410, height=410) self.canvas = tkinter.Canvas(self.w, width=400, height=400) self.paint() self.canvas.bind("<Button-1>", self.click) self.canvas.bind("<Motion>", self.moved) self.w.pack() def toCartesian(self, y): """Convert tkinter point into Cartesian.""" return self.w.winfo_height() - y def toTk(self,y): """Convert Cartesian into tkinter point.""" if y == maxValue: return 0 tk_y = self.w.winfo_height() if y != minValue: tk_y -= y return tk_y def moved(self, event): """React to mouse move events.""" if self.static: self.paint() return p = (event.x, self.toCartesian(event.y)) match = self.tree.find(p) if match: p = match.point self.canvas.create_rectangle(p[X] - RectangleSize, self.toTk(p[Y]) - RectangleSize, p[X] + RectangleSize, self.toTk(p[Y]) + RectangleSize, fill='Red', tags=closest) self.canvas.delete(self.shortline) self.shortline = None else: self.canvas.delete(closest) n = self.tree.nearest(p) if n: pn = n.point if self.shortline is None: self.shortline = self.canvas.create_line(pn[X], self.toTk(pn[Y]), p[X], self.toTk(p[Y]), fill='Red', dash=(2, 4), tags=shortline) else: self.canvas.coords(shortline, pn[X], self.toTk(pn[Y]), p[X], self.toTk(p[Y])) def click(self, event): """Add point to KDTree.""" p = (event.x, self.toCartesian(event.y)) self.tree.add(p) if self.shortline: self.canvas.delete(self.shortline) self.shortline = None self.paint() def drawPartition (self, r, p, orient): """Draw proper partition for given node (r,p) and orientation.""" if orient == VERTICAL: self.canvas.create_line(p[X], self.toTk(r.y_min), p[X], self.toTk(r.y_max)) else: xlow = r.x_min if r.x_min <= minValue: xlow = 0 xhigh = r.x_max if r.x_max >= maxValue: xhigh = self.w.winfo_width() self.canvas.create_line(xlow, self.toTk(p[Y]), xhigh, self.toTk(p[Y])) self.canvas.create_rectangle(p[X] - RectangleSize, self.toTk(p[Y]) - RectangleSize, p[X] + RectangleSize, self.toTk(p[Y]) + RectangleSize, fill='Black') def visit (self, n): """Draw KDNode properly partitioned and its sub-children.""" if n == None: return self.drawPartition(n.region, n.point, n.orient) self.visit (n.below) self.visit (n.above) def prepare(self, event): """prepare to add points.""" if self.label: self.label.destroy() self.label = None self.canvas.pack() def paint(self): """Paint KDTree by visiting all nodes, or show introductory message.""" if self.tree.root: self.canvas.delete(tkinter.ALL) self.visit(self.tree.root) else: self.label = tkinter.Label(self.w, width=100, height = 40, text="Click To Add Points") self.label.bind("<Button-1>", self.prepare) self.label.pack()