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 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 find_circle_center(a, b, c): # check if bc is a "right turn" from ab - det if ((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y)) < 0: return 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 None, None # Points are co-linear # point o is the center of the circle ox = (D * E - B * F) / G oy = (A * F - C * E) / G max_x = ox + math.sqrt((a.x - ox)**2 + (a.y - oy)**2) o = Point(ox, oy) return max_x, o
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 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 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 calculate_parabolas_intersection(parabola1, parabola2, sweep_line): z0 = 2.0 * (parabola1.x - sweep_line) z1 = 2.0 * (parabola2.x - sweep_line) a = 1.0 / z0 - 1.0 / z1 b = -2.0 * (parabola1.y / z0 - parabola2.y / z1) c = 1.0 * (parabola1.y**2 + parabola1.x**2 - sweep_line**2) / z0 - 1.0 * ( parabola2.y**2 + parabola2.x**2 - sweep_line**2) / z1 py = 1.0 * (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a) px = 1.0 * (parabola1.x**2 + (parabola1.y - py)**2 - sweep_line**2) / (2 * parabola1.x - 2 * sweep_line) return Point(px, py)
def find_intersection_of_parabolas(parabola1, parabola2, sweep_line): p = parabola1 if parabola1.x == parabola2.x: py = (parabola1.y + parabola2.y) / 2.0 elif parabola2.x == sweep_line: py = parabola2.y elif parabola1.x == sweep_line: py = parabola1.y p = parabola2 else: return calculate_parabolas_intersection(parabola1, parabola2, sweep_line) px = (p.x**2 + (p.y - py)**2 - sweep_line**2) / (2 * p.x - 2 * sweep_line) res = Point(px, py) return res
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 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 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 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 check_if_arc_intersects(point, arc): if arc is None: return None if arc.point.x == point.x: return None upper_intersection_y = 0 lower_intersection_y = 0 if arc.prev is not None: upper_intersection_y = (find_intersection_of_parabolas( arc.prev.point, arc.point, point.x)).y if arc.next is not None: lower_intersection_y = (find_intersection_of_parabolas( arc.point, arc.next.point, point.x)).y if (arc.prev is None or upper_intersection_y <= point.y) and ( arc.next is None or point.y <= lower_intersection_y): py = point.y px = (arc.point.x**2 + (arc.point.y - py)**2 - point.x**2) / (2 * arc.point.x - 2 * point.x) return Point(px, py) return None