def __init__(self, points): self.output = [] # list of line segment self.arc = None # binary tree for parabola arcs self.points = PriorityQueue() # site events self.event = PriorityQueue() # circle events # bounding box self.x0 = -50.0 self.x1 = -50.0 self.y0 = 550.0 self.y1 = 550.0 # insert points to site event for pts in points: point = Point(pts[0], pts[1]) self.points.push(point) # keep track of bounding box size if point.x < self.x0: self.x0 = point.x if point.y < self.y0: self.y0 = point.y if point.x > self.x1: self.x1 = point.x if point.y > self.y1: self.y1 = point.y # add margins to the bounding box dx = (self.x1 - self.x0 + 1) / 5.0 dy = (self.y1 - self.y0 + 1) / 5.0 self.x0 = self.x0 - dx self.x1 = self.x1 + dx self.y0 = self.y0 - dy self.y1 = self.y1 + dy
def __init__(self, points): self.voronoi = [] self.beach_line = None self.scenes = [] self.points = points self.events = PriorityQueue() # bounding box self.LEFT = -50 self.RIGHT = -50 self.TOP = 500 self.BOTTOM = 500 self.create_bounding_box(points)
class Voronoi: def __init__(self, points): self.output = [] # list of line segment self.arc = None # binary tree for parabola arcs self.points = PriorityQueue() # site events self.event = PriorityQueue() # circle events # bounding box self.x0 = -50.0 self.x1 = -50.0 self.y0 = 550.0 self.y1 = 550.0 # insert points to site event for pts in points: point = Point(pts[0], pts[1]) self.points.push(point) # keep track of bounding box size if point.x < self.x0: self.x0 = point.x if point.y < self.y0: self.y0 = point.y if point.x > self.x1: self.x1 = point.x if point.y > self.y1: self.y1 = point.y # add margins to the bounding box dx = (self.x1 - self.x0 + 1) / 5.0 dy = (self.y1 - self.y0 + 1) / 5.0 self.x0 = self.x0 - dx self.x1 = self.x1 + dx self.y0 = self.y0 - dy self.y1 = self.y1 + dy def process(self): while not self.points.empty(): if not self.event.empty() and (self.event.top().x <= self.points.top().x): self.process_event() # handle circle event else: self.process_point() # handle site event # after all points, process remaining circle events while not self.event.empty(): self.process_event() self.finish_edges() def process_point(self): # get next event from site pq p = self.points.pop() # add new arc (parabola) self.arc_insert(p) def process_event(self): # get next event from circle pq e = self.event.pop() if e.valid: # start new edge s = Segment(e.p) self.output.append(s) # remove associated arc (parabola) a = e.a if a.pprev is not None: a.pprev.pnext = a.pnext a.pprev.s1 = s if a.pnext is not None: a.pnext.pprev = a.pprev a.pnext.s0 = s # finish the edges before and after a if a.s0 is not None: a.s0.finish(e.p) if a.s1 is not None: a.s1.finish(e.p) # recheck circle events on either side of p if a.pprev is not None: self.check_circle_event(a.pprev, e.x) if a.pnext is not None: self.check_circle_event(a.pnext, e.x) def arc_insert(self, p): if self.arc is None: self.arc = Arc(p) else: # find the current arcs at p.y i = self.arc while i is not None: flag, z = self.intersect(p, i) if flag: # new parabola intersects arc i flag, zz = self.intersect(p, i.pnext) if (i.pnext is not None) and (not flag): i.pnext.pprev = Arc(i.p, i, i.pnext) i.pnext = i.pnext.pprev else: i.pnext = Arc(i.p, i) i.pnext.s1 = i.s1 # add p between i and i.pnext i.pnext.pprev = Arc(p, i, i.pnext) i.pnext = i.pnext.pprev i = i.pnext # now i points to the new arc # add new half-edges connected to i's endpoints seg = Segment(z) self.output.append(seg) i.pprev.s1 = i.s0 = seg seg = Segment(z) self.output.append(seg) i.pnext.s0 = i.s1 = seg # check for new circle events around the new arc self.check_circle_event(i, p.x) self.check_circle_event(i.pprev, p.x) self.check_circle_event(i.pnext, p.x) return i = i.pnext # if p never intersects an arc, append it to the list i = self.arc while i.pnext is not None: i = i.pnext i.pnext = Arc(p, i) # insert new segment between p and i x = self.x0 y = (i.pnext.p.y + i.p.y) / 2.0; start = Point(x, y) seg = Segment(start) i.s1 = i.pnext.s0 = seg self.output.append(seg) def check_circle_event(self, i, x0): # look for a new circle event for arc i if (i.e is not None) and (i.e.x != self.x0): i.e.valid = False i.e = None if (i.pprev is None) or (i.pnext is None): return flag, x, o = self.circle(i.pprev.p, i.p, i.pnext.p) if flag and (x > self.x0): i.e = Event(x, o, i) self.event.push(i.e) def circle(self, a, b, c): # check if bc is a "right turn" from ab if ((b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y)) > 0: return False, None, None # Joseph O'Rourke, Computational Geometry in C (2nd ed.) p.189 A = b.x - a.x B = b.y - a.y C = c.x - a.x D = c.y - a.y E = A*(a.x + b.x) + B*(a.y + b.y) F = C*(a.x + c.x) + D*(a.y + c.y) G = 2*(A*(c.y - b.y) - B*(c.x - b.x)) if (G == 0): return False, None, None # Points are co-linear # point o is the center of the circle ox = 1.0 * (D*E - B*F) / G oy = 1.0 * (A*F - C*E) / G # o.x plus radius equals max x coord x = ox + math.sqrt((a.x-ox)**2 + (a.y-oy)**2) o = Point(ox, oy) return True, x, o def intersect(self, p, i): # check whether a new parabola at point p intersect with arc i if (i is None): return False, None if (i.p.x == p.x): return False, None a = 0.0 b = 0.0 if i.pprev is not None: a = (self.intersection(i.pprev.p, i.p, 1.0*p.x)).y if i.pnext is not None: b = (self.intersection(i.p, i.pnext.p, 1.0*p.x)).y if (((i.pprev is None) or (a <= p.y)) and ((i.pnext is None) or (p.y <= b))): py = p.y px = 1.0 * ((i.p.x)**2 + (i.p.y-py)**2 - p.x**2) / (2*i.p.x - 2*p.x) res = Point(px, py) return True, res return False, None def intersection(self, p0, p1, l): # get the intersection of two parabolas p = p0 if (p0.x == p1.x): py = (p0.y + p1.y) / 2.0 elif (p1.x == l): py = p1.y elif (p0.x == l): py = p0.y p = p1 else: # use quadratic formula z0 = 2.0 * (p0.x - l) z1 = 2.0 * (p1.x - l) a = 1.0/z0 - 1.0/z1 b = -2.0 * (p0.y/z0 - p1.y/z1) c = 1.0 * (p0.y**2 + p0.x**2 - l**2) / z0 - 1.0 * (p1.y**2 + p1.x**2 - l**2) / z1 py = 1.0 * (-b-math.sqrt(b*b - 4*a*c)) / (2*a) px = 1.0 * (p.x**2 + (p.y-py)**2 - l**2) / (2*p.x-2*l) res = Point(px, py) return res def finish_edges(self): l = self.x1 + (self.x1 - self.x0) + (self.y1 - self.y0) i = self.arc while i.pnext is not None: if i.s1 is not None: p = self.intersection(i.p, i.pnext.p, l*2.0) i.s1.finish(p) i = i.pnext def print_output(self): it = 0 for o in self.output: it = it + 1 p0 = o.start p1 = o.end print (p0.x, p0.y, p1.x, p1.y) def get_output(self): res = [] for o in self.output: p0 = o.start p1 = o.end res.append((p0.x, p0.y, p1.x, p1.y)) return res
class Voronoi: def __init__(self, points): self.output = [] # list of line segment self.arc = None # binary tree for parabola arcs self.points = PriorityQueue() # site events self.event = PriorityQueue() # circle events # bounding box self.x0 = -50.0 self.x1 = -50.0 self.y0 = 550.0 self.y1 = 550.0 # insert points to site event for pts in points: point = Point(pts[0], pts[1]) self.points.push(point) # keep track of bounding box size if point.x < self.x0: self.x0 = point.x if point.y < self.y0: self.y0 = point.y if point.x > self.x1: self.x1 = point.x if point.y > self.y1: self.y1 = point.y # add margins to the bounding box dx = (self.x1 - self.x0 + 1) / 5.0 dy = (self.y1 - self.y0 + 1) / 5.0 self.x0 = self.x0 - dx self.x1 = self.x1 + dx self.y0 = self.y0 - dy self.y1 = self.y1 + dy def process(self): while not self.points.empty(): if not self.event.empty() and (self.event.top().x <= self.points.top().x): self.process_event() # handle circle event else: self.process_point() # handle site event # after all points, process remaining circle events while not self.event.empty(): self.process_event() self.finish_edges() def process_point(self): # get next event from site pq p = self.points.pop() # add new arc (parabola) self.arc_insert(p) def process_event(self): # get next event from circle pq e = self.event.pop() if e.valid: # start new edge s = Segment(e.p) self.output.append(s) # remove associated arc (parabola) a = e.a if a.pprev is not None: a.pprev.pnext = a.pnext a.pprev.s1 = s if a.pnext is not None: a.pnext.pprev = a.pprev a.pnext.s0 = s # finish the edges before and after a if a.s0 is not None: a.s0.finish(e.p) if a.s1 is not None: a.s1.finish(e.p) # recheck circle events on either side of p if a.pprev is not None: self.check_circle_event(a.pprev, e.x) if a.pnext is not None: self.check_circle_event(a.pnext, e.x) def arc_insert(self, p): if self.arc is None: self.arc = Arc(p) else: # find the current arcs at p.y i = self.arc while i is not None: flag, z = self.intersect(p, i) if flag: # new parabola intersects arc i flag, zz = self.intersect(p, i.pnext) if (i.pnext is not None) and (not flag): i.pnext.pprev = Arc(i.p, i, i.pnext) i.pnext = i.pnext.pprev else: i.pnext = Arc(i.p, i) i.pnext.s1 = i.s1 # add p between i and i.pnext i.pnext.pprev = Arc(p, i, i.pnext) i.pnext = i.pnext.pprev i = i.pnext # now i points to the new arc # add new half-edges connected to i's endpoints seg = Segment(z) self.output.append(seg) i.pprev.s1 = i.s0 = seg seg = Segment(z) self.output.append(seg) i.pnext.s0 = i.s1 = seg # check for new circle events around the new arc self.check_circle_event(i, p.x) self.check_circle_event(i.pprev, p.x) self.check_circle_event(i.pnext, p.x) return i = i.pnext # if p never intersects an arc, append it to the list i = self.arc while i.pnext is not None: i = i.pnext i.pnext = Arc(p, i) # insert new segment between p and i x = self.x0 y = (i.pnext.p.y + i.p.y) / 2.0; start = Point(x, y) seg = Segment(start) i.s1 = i.pnext.s0 = seg self.output.append(seg) def check_circle_event(self, i, x0): # look for a new circle event for arc i if (i.e is not None) and (i.e.x <> self.x0): i.e.valid = False i.e = None if (i.pprev is None) or (i.pnext is None): return flag, x, o = self.circle(i.pprev.p, i.p, i.pnext.p) if flag and (x > self.x0): i.e = Event(x, o, i) self.event.push(i.e) def circle(self, a, b, c): # check if bc is a "right turn" from ab if ((b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y)) > 0: return False, None, None # Joseph O'Rourke, Computational Geometry in C (2nd ed.) p.189 A = b.x - a.x B = b.y - a.y C = c.x - a.x D = c.y - a.y E = A*(a.x + b.x) + B*(a.y + b.y) F = C*(a.x + c.x) + D*(a.y + c.y) G = 2*(A*(c.y - b.y) - B*(c.x - b.x)) if (G == 0): return False, None, None # Points are co-linear # point o is the center of the circle ox = 1.0 * (D*E - B*F) / G oy = 1.0 * (A*F - C*E) / G # o.x plus radius equals max x coord x = ox + math.sqrt((a.x-ox)**2 + (a.y-oy)**2) o = Point(ox, oy) return True, x, o def intersect(self, p, i): # check whether a new parabola at point p intersect with arc i if (i is None): return False, None if (i.p.x == p.x): return False, None a = 0.0 b = 0.0 if i.pprev is not None: a = (self.intersection(i.pprev.p, i.p, 1.0*p.x)).y if i.pnext is not None: b = (self.intersection(i.p, i.pnext.p, 1.0*p.x)).y if (((i.pprev is None) or (a <= p.y)) and ((i.pnext is None) or (p.y <= b))): py = p.y px = 1.0 * ((i.p.x)**2 + (i.p.y-py)**2 - p.x**2) / (2*i.p.x - 2*p.x) res = Point(px, py) return True, res return False, None def intersection(self, p0, p1, l): # get the intersection of two parabolas p = p0 if (p0.x == p1.x): py = (p0.y + p1.y) / 2.0 elif (p1.x == l): py = p1.y elif (p0.x == l): py = p0.y p = p1 else: # use quadratic formula z0 = 2.0 * (p0.x - l) z1 = 2.0 * (p1.x - l) a = 1.0/z0 - 1.0/z1; b = -2.0 * (p0.y/z0 - p1.y/z1) c = 1.0 * (p0.y**2 + p0.x**2 - l**2) / z0 - 1.0 * (p1.y**2 + p1.x**2 - l**2) / z1 py = 1.0 * (-b-math.sqrt(b*b - 4*a*c)) / (2*a) px = 1.0 * (p.x**2 + (p.y-py)**2 - l**2) / (2*p.x-2*l) res = Point(px, py) return res def finish_edges(self): l = self.x1 + (self.x1 - self.x0) + (self.y1 - self.y0) i = self.arc while i.pnext is not None: if i.s1 is not None: p = self.intersection(i.p, i.pnext.p, l*2.0) i.s1.finish(p) i = i.pnext def print_output(self): it = 0 for o in self.output: it = it + 1 p0 = o.start p1 = o.end print (p0.x, p0.y, p1.x, p1.y) def get_output(self): res = [] for o in self.output: p0 = o.start p1 = o.end res.append((p0.x, p0.y, p1.x, p1.y)) return res
class Fortune: def __init__(self, points): self.voronoi = [] self.beach_line = None self.scenes = [] self.points = points self.events = PriorityQueue() # bounding box self.LEFT = -50 self.RIGHT = -50 self.TOP = 500 self.BOTTOM = 500 self.create_bounding_box(points) def create_bounding_box(self, points): for pts in points: point = Point(pts[0], pts[1]) self.events.push(point) if point.x < self.LEFT: self.LEFT = point.x if point.y < self.TOP: self.TOP = point.y if point.x > self.RIGHT: self.RIGHT = point.x if point.y > self.BOTTOM: self.BOTTOM = point.y dx = (self.RIGHT - self.LEFT + 1) / 5.0 dy = (self.BOTTOM - self.TOP + 1) / 5.0 self.LEFT = self.LEFT - dx self.RIGHT = self.RIGHT + dx self.TOP = self.TOP - dy self.BOTTOM = self.BOTTOM + dy def calculate_voronoi_diagram(self): while not self.events.empty(): event = self.events.pop() if isinstance(event, Circle): self.handle_circle_event(event) else: self.handle_point_event(event) self.finish_edges() def calculate_voronoi_diagram_with_visualization(self): while not self.events.empty(): event = self.events.pop() if isinstance(event, Circle): self.handle_circle_event(event) else: self.handle_point_event(event) self.scenes.append( Scene([ PointsCollection(self.points, color='red'), PointsCollection(self.get_voronoi_points(), color='blue') ], [LinesCollection(self.get_voronoi_lines())])) self.finish_edges_with_visualization() def handle_point_event(self, point): self.insert_arc(point) def handle_circle_event(self, circle_event): if circle_event.valid: edge = Edge(circle_event.point) self.voronoi.append(edge) arc = circle_event.beach_line if arc.prev is not None: arc.prev.next = arc.next arc.prev.lower_edge = edge if arc.next is not None: arc.next.prev = arc.prev arc.next.upper_edge = edge if arc.upper_edge is not None: arc.upper_edge.finish(circle_event.point) if arc.lower_edge is not None: arc.lower_edge.finish(circle_event.point) if arc.prev is not None: self.check_circle_event(arc.prev) if arc.next is not None: self.check_circle_event(arc.next) def insert_arc(self, point): if self.beach_line is None: self.beach_line = Arc(point) return if self.insert_arc_among_existing(point): return arc = self.beach_line while arc.next is not None: arc = arc.next arc.next = Arc(point, arc) x = self.LEFT y = (arc.next.point.y + arc.point.y) / 2.0 start = Point(x, y) edge = Edge(start) arc.lower_edge = arc.next.upper_edge = edge self.voronoi.append(edge) def insert_arc_among_existing(self, point): arc = self.beach_line while arc is not None: intersection_point = check_if_arc_intersects(point, arc) if intersection_point is not None: second_intersection_point = check_if_arc_intersects( point, arc.next) if arc.next is not None and second_intersection_point is None: arc.next.prev = Arc(arc.point, arc, arc.next) arc.next = arc.next.prev else: arc.next = Arc(arc.point, arc) arc.next.lower_edge = arc.lower_edge arc.next.prev = Arc(point, arc, arc.next) arc.next = arc.next.prev arc = arc.next edge = Edge(intersection_point) self.voronoi.append(edge) arc.prev.lower_edge = arc.upper_edge = edge edge = Edge(intersection_point) self.voronoi.append(edge) arc.next.upper_edge = arc.lower_edge = edge self.check_circle_event(arc) self.check_circle_event(arc.prev) self.check_circle_event(arc.next) return True arc = arc.next return False def check_circle_event(self, arc): if arc.circle_event is not None and arc.circle_event.x != self.LEFT: arc.circle_event.valid = False arc.circle_event = None if arc.prev is None or arc.next is None: return max_x, center = find_circle_center(arc.prev.point, arc.point, arc.next.point) if center is not None and max_x > self.LEFT: arc.circle_event = Circle(max_x, center, arc) self.events.push(arc.circle_event) def finish_edges(self): limit = self.RIGHT + (self.RIGHT - self.LEFT) + (self.BOTTOM - self.TOP) arc = self.beach_line while arc.next is not None: if arc.lower_edge is not None: point = find_intersection_of_parabolas(arc.point, arc.next.point, limit * 2.0) arc.lower_edge.finish(Point(-1 * point.x, -1 * point.y)) arc = arc.next def finish_edges_with_visualization(self): limit = self.RIGHT + (self.RIGHT - self.LEFT) + (self.BOTTOM - self.TOP) arc = self.beach_line while arc.next is not None: if arc.lower_edge is not None: point = find_intersection_of_parabolas(arc.point, arc.next.point, limit * 2.0) arc.lower_edge.finish(Point(-1 * point.x, -1 * point.y)) self.scenes.append( Scene([ PointsCollection(self.points, color='red'), PointsCollection(self.get_voronoi_points(), color='blue') ], [LinesCollection(self.get_voronoi_lines())])) arc = arc.next def print_output(self): for edge in self.voronoi: if edge.done: print(edge.start.x, edge.start.y, edge.end.x, edge.end.y) else: print(edge.start.x, edge.start.y) def get_voronoi_points(self): result = [] for edge in self.voronoi: if edge.done: result.append((edge.start.x, edge.start.y)) result.append((edge.end.x, edge.end.y)) else: result.append((edge.start.x, edge.start.y)) return result def get_voronoi_lines(self): result = [] for edge in self.voronoi: if edge.done: result.append([(edge.start.x, edge.start.y), (edge.end.x, edge.end.y)]) return result