def find_tangent(VDL, VDR): pl = VDL.parent.points[VDL.range_points[1]] pr = VDR.parent.points[VDR.range_points[0]] #handle collinear point while not (VD.isupper_tangent(pl, pr, 'left') and VD.isupper_tangent(pl, pr, 'right')): while not VD.isupper_tangent(pl, pr, 'left'): pl = pl.ccw while not VD.isupper_tangent(pl, pr, 'right'): pr = pr.cw upper_tangent = Line(pl, pr) #VDL.parent.msg = VDL.parent.msg + 'upper_tangent = '+upper_tangent.p1.display+' '+upper_tangent.p2.display+'\n' pl = VDL.parent.points[VDL.range_points[1]] pr = VDR.parent.points[VDR.range_points[0]] while not (VD.islower_tangent(pl, pr, 'left') and VD.islower_tangent(pl, pr, 'right')): while not VD.islower_tangent(pl, pr, 'left'): pl = pl.cw while not VD.islower_tangent(pl, pr, 'right'): pr = pr.ccw lower_tangent = Line(pl, pr) #VDL.parent.msg = VDL.parent.msg+'lower_tangent = '+lower_tangent.p1.display+' '+lower_tangent.p2.display+'\n' return (upper_tangent, lower_tangent)
def step_by_step(self): self.parent.step_button.setEnabled(True) if not self.isstep_by_step == True: self.isstep_by_step = True self.prepare() final = self.Voronoi((0,len(self.points)-1)) #for run after step self.lines = final.lines self.vertex = final.convex.vertex Line.intersect_with_edge(final.lines,Canvas.edge_painter) #final_vd = VD(copy.deepcopy(final.lines),copy.deepcopy(final.range_points),self) final_vd = savevd(final.lines,final.range_points,self,final.convex) final_vd.pos = 'final' final_vd.color = QtCore.Qt.black self.vd[0].append(final_vd) self.repaint() if self.vd[1] == len(self.vd[0])-1: self.drawDisplay.display_output() self.vd[1] = self.vd[1]+1 if self.vd[1] >= len(self.vd[0]): self.parent.step_button.setEnabled(False) self.vd = [[],0] self.hp = [[],0] self.isstep_by_step = False
def test_cross_point_line(self): self.assertEqual( Circle(Point(2, 3), 2).cross_point_line(Line(Point(0, 3), Point(1, 3))), [Point(0, 3), Point(4, 3)]) self.assertEqual( Circle(Point(2, 3), 2).cross_point_line(Line(Point(4, 0), Point(4, 6))), [Point(4, 3), Point(4, 3)])
def savevd(lines,range_points,self,convex): line = [] for l in traverse(lines): p1 = copy.deepcopy(l.p1) p2 = copy.deepcopy(l.p2) t = Line(p1,p2) t.avail = l.avail line.append(t) return VD(line,range_points,self,convex = convex)
def main() -> None: x1, y1, x2, y2 = [int(x) for x in input().split()] p1 = Point(x1, y1) p2 = Point(x2, y2) l = Line(p1, p2) q = int(input()) for _ in range(q): x, y = [int(x) for x in input().split()] p = Point(x, y) a = l.reflection(p) print(a.x, a.y)
def prepare(size, polygons_info, start, end): ''' something neccesary such as construct the whole picture ''' polygons = generate_polygons(*polygons_info) for each in polygons: add_polygon_to_plot(each) start = Point(start) end = Point(end) points = [start] for polygon in polygons: points += polygon.vertices points += [end] #get successor_and_costs for every points for i in xrange(len(points)-1): # -1 for end point does not need to get successor point_i = points[i] siblings = [point_i] belonging = check_belonging(point_i, polygons) if belonging[0]: for each in belonging[1]: point_i.add_successor(each) siblings = belonging[2].vertices for other_point in points: if other_point in siblings: continue flag = True for polygon in polygons: if polygon.whether_intersect_line(Line((point_i, other_point))): flag = False break if flag: point_i.add_successor(other_point) return points
def read_output(self): self.painter.drawDisplay.ClearCanvas() filename, _ = QtWidgets.QFileDialog.getOpenFileName() if filename: f = open(filename, 'r') lines = f.readlines() points = [] _points = [] for l in lines: t = l.split() if t[0] == 'P': #need to create new attribute to save original point set which include duplicates t1 = int(t[1]) t2 = int(t[2]) p = Point(t1, t2) _points.append(p) if p not in self.painter.points: points.append(p) self.painter.points.add((Point(t1, t2))) elif t[0] == 'E': self.painter.lines.append( Line(Point(int(t[1]), int(t[2])), Point(int(t[3]), int(t[4])))) self.painter.points = points self.painter._points = _points for p in _points: self.painter.drawDisplay.display_points(p) self.painter.drawDisplay.display_output() self.painter.update() f.close()
def set_self(self, start, end, image_dict, colour): self.line = Line(start, end) self.colour = colour line_length = int( round(pythagory(self.line.vector[0], self.line.vector[1]))) w = line_length + 2 * PLATFORM.BORDER + 2 * PLATFORM.SIDE_GAP h = PLATFORM.IMAGE_HEIGHT base_image = pygame.Surface([w, h]) base_image = base_image.convert_alpha() base_image.fill((1, 1, 1, 0)) base_image.blit(image_dict['platform' + colour + '-left'], (0, 0)) side_w = image_dict['platform' + colour + '-left'].get_width() for x in range(line_length / PLATFORM.MIDDLE_WIDTH): base_image.blit(image_dict['platform' + colour + '-middle'], (side_w + x * PLATFORM.MIDDLE_WIDTH, 0)) #if line_length%PLATFORM.MIDDLE_WIDTH != 0: base_image.blit( image_dict['platform' + colour + '-middle'], (side_w + (line_length / PLATFORM.MIDDLE_WIDTH) * PLATFORM.MIDDLE_WIDTH, 0), pygame.Rect(0, 0, line_length % PLATFORM.MIDDLE_WIDTH, PLATFORM.IMAGE_HEIGHT)) base_image.blit(image_dict['platform' + colour + '-right'], (w - side_w, 0)) self.image = pygame.transform.rotozoom( base_image, -vector_to_angle(self.line.vector) - 90, 1.0) self.image_position = start[ 0] - self.line.vector[0] / 2 - self.image.get_width() / 2, start[ 1] - self.line.vector[1] / 2 - self.image.get_height() / 2
def draw(self, vis_graph): '''Draws the shape and a line connecting the node to its parent''' self.shape.draw() if self.node.parent != None: if self.is_path is True: parent = vis_graph.get_visual(self.node.parent) parent_line = Line(self.position, parent.position, self.shape.surface, (150,255,25),1) parent_line.draw() else: parent = vis_graph.get_visual(self.node.parent) parent_line = Line(self.position, parent.position, self.shape.surface, (150,50,50),1) parent_line.draw()
def createLine(self, x1, y1, x2, y2, strokewidth=1, stroke="black"): """ Creates a line @type x1: string or int @param x1: starting x-coordinate @type y1: string or int @param y1: starting y-coordinate @type x2: string or int @param x2: ending x-coordinate @type y2: string or int @param y2: ending y-coordinate @type strokewidth: string or int @param strokewidth: width of the pen used to draw @type stroke: string (either css constants like "black" or numerical values like "#FFFFFF") @param stroke: color with which to draw the outer limits @return: a line object """ style_dict = {'stroke-width': strokewidth, 'stroke': stroke} myStyle = StyleBuilder(style_dict) l = Line(x1, y1, x2, y2) l.set_style(myStyle.getStyle()) return l
def Run(self): if self.isstep_by_step == False: self.prepare() ans = self.Voronoi((0,len(self.points)-1)) Line.intersect_with_edge(ans.lines,Canvas.edge_painter) self.lines = ans.lines self.vertex = ans.convex.vertex self.drawDisplay.display_output() self.update() else: #current state is step_by_step, switch to run all vd while True: self.parent.step_button.setEnabled(False) self.repaint() if self.vd[1] == len(self.vd[0])-1: self.drawDisplay.display_output() time.sleep(1) self.vd[1] = self.vd[1]+1 if self.vd[1] >= len(self.vd[0]): break self.vd = [[],0] self.hp = [[],0] self.isstep_by_step = False
def nextPoint(pool,SG_bisector): ans = None first = True for candidate in pool: if candidate.line.avail== True and SG_bisector.p1 is not candidate.line.hole: result = Line.intersect(candidate.line,SG_bisector) if result is not None: t = (result,candidate.point,candidate.line) if first == True: ans = t first = False else: if t[0].y <= ans[0].y: ans = t return ans
def nextPoint(pool, SG_bisector): ans = None first = True for candidate in pool: if candidate.line.avail == True and SG_bisector.p1 is not candidate.line.hole: result = Line.intersect(candidate.line, SG_bisector) if result is not None: t = (result, candidate.point, candidate.line) if first == True: ans = t first = False else: if t[0].y <= ans[0].y: ans = t return ans
def __init__(self, type, img_dict, obj, position_or_angle): Monster.__init__(self) self.type = type self.has_line = True self.border = MONSTER.EYE.RADIUS self.frame = 0 if self.type == MONSTER.TYPE_ANGLE: # count start_position self.start = sum(obj.circle.center, multiply(angle_to_vector(position_or_angle), obj.circle.radius + MONSTER.EYE.RADIUS)) self.angle = position_or_angle self.vector = angle_to_vector(self.angle) else: self.vector = inverse(perpendicular(normalise(obj.line.vector))) shift = multiply(obj.line.vector, -position_or_angle/100.0) # side shift self.start = sum(sum(obj.line.start, multiply(self.vector, PLATFORM.BORDER - 3 + MONSTER.EYE.RADIUS)), shift) self.angle = vector_to_angle(self.vector) end = sum(self.start, multiply(self.vector, MONSTER.EYE.LINE_LENGTH)) self.line = Line(self.start, end) self.img_list = [] for i in img_dict['eye']: self.img_list.append(pygame.transform.rotozoom(i, -self.angle,1.)) self.shift_constant = img_dict['eye'][0].get_height()/2 - MONSTER.EYE.RADIUS self.image_offset = subtract(multiply(self.vector, self.shift_constant), (self.img_list[0].get_width()/2, self.img_list[0].get_height()/2)) self.image_position = sum(self.start, self.image_offset) self.image = self.img_list[0]
def clip(): lower = range_points[0] upper = range_points[1] lines = [] dis = [] t = 0 mid = [] for i in range(lower,upper): for j in range(i+1,upper+1): lines.append(Line.biSector(self.points[i],self.points[j])) self.points[i].related.append(pair(lines[-1],self.points[j])) self.points[j].related.append(pair(lines[-1],self.points[i])) lines[-1]._p1 = self.points[i] lines[-1]._p2 = self.points[j] mid.append(((self.points[i]+self.points[j])/2,t)) dis.append((t,(self.points[i].x-self.points[j].x)**2+(self.points[i].y-self.points[j].y)**2,Line(self.points[i],self.points[j]))) t = t+1 circumcenter = Line.intersect(lines[0],lines[1]) if circumcenter is not None: tmp_lines = None dis.sort(key = lambda x : x[1]) triangle = 'acute' if dis[0][1]+dis[1][1] == dis[2][1]: triangle = 'right' tmp_lines = dis[0:2] elif dis[0][1]+dis[1][1]<dis[2][1]: triangle = 'obtuse' #print triangle s = dis[2][0] t = 0 for i in range(lower,upper): for j in range(i+1,upper+1): ab = (Line(self.points[i],self.points[j])) hl = Line(lines[t].p1,circumcenter) result = Line.intersect(hl,ab) #do not determine the longest side of right triangle in the same way if not (triangle == 'right' and t == s): if result is None: #reverse policy for longest side of obtuse if not (triangle == 'obtuse' and t == s): lines[t].p1 = circumcenter else: lines[t].p2 = circumcenter else: if not (triangle == 'obtuse' and t == s): lines[t].p2 = circumcenter else: lines[t].p1 = circumcenter t = t+1 #now determine the longest side of right triangle if triangle == 'right': t = dis[2][0] if Line.intersect(Line(lines[t].p1,circumcenter),tmp_lines[0][2]) != None or Line.intersect(Line(lines[t].p1,circumcenter),tmp_lines[1][2]) != None: lines[t].p1,lines[t].p2 = circumcenter,lines[t].p2 else: lines[t].p1,lines[t].p2 = circumcenter,lines[t].p1 #create circumcenter related points circumcenter.iscircumcenter = True lines[0].connected.append(lines[1]) lines[0].connected.append(lines[2]) lines[1].connected.append(lines[0]) lines[1].connected.append(lines[2]) lines[2].connected.append(lines[0]) lines[2].connected.append(lines[1]) else: mid.sort(key = lambda s: attrgetter('x','y')(s[0])) t = mid[1][1] del_line = lines[t] for i in range(lower,upper+1): u = self.points[i].related for j in range(0,len(u)): if u[j].line is del_line: del u[j] break del lines[t] #Line.intersect_with_edge(lines,Canvas.edge_painter,'colinear') return lines
def Voronoi(self,range_points): if (range_points[1]-range_points[0]+1) == 2: #print 'len = 2' lower = range_points[0] upper = range_points[1] line = [Line.biSector(self.points[lower],self.points[upper])] line[0]._p1 = self.points[lower] line[0]._p2 = self.points[upper] #hash table for point mapping to related biSector self.points[lower].related.append(pair(line[0],self.points[upper])) self.points[upper].related.append(pair(line[0],self.points[lower])) #Line.intersect_with_edge(line,Canvas.edge_painter,'colinear') vd = VD(line,range_points,self) return vd elif (range_points[1]-range_points[0]+1) == 3: #print 'len = 3' def clip(): lower = range_points[0] upper = range_points[1] lines = [] dis = [] t = 0 mid = [] for i in range(lower,upper): for j in range(i+1,upper+1): lines.append(Line.biSector(self.points[i],self.points[j])) self.points[i].related.append(pair(lines[-1],self.points[j])) self.points[j].related.append(pair(lines[-1],self.points[i])) lines[-1]._p1 = self.points[i] lines[-1]._p2 = self.points[j] mid.append(((self.points[i]+self.points[j])/2,t)) dis.append((t,(self.points[i].x-self.points[j].x)**2+(self.points[i].y-self.points[j].y)**2,Line(self.points[i],self.points[j]))) t = t+1 circumcenter = Line.intersect(lines[0],lines[1]) if circumcenter is not None: tmp_lines = None dis.sort(key = lambda x : x[1]) triangle = 'acute' if dis[0][1]+dis[1][1] == dis[2][1]: triangle = 'right' tmp_lines = dis[0:2] elif dis[0][1]+dis[1][1]<dis[2][1]: triangle = 'obtuse' #print triangle s = dis[2][0] t = 0 for i in range(lower,upper): for j in range(i+1,upper+1): ab = (Line(self.points[i],self.points[j])) hl = Line(lines[t].p1,circumcenter) result = Line.intersect(hl,ab) #do not determine the longest side of right triangle in the same way if not (triangle == 'right' and t == s): if result is None: #reverse policy for longest side of obtuse if not (triangle == 'obtuse' and t == s): lines[t].p1 = circumcenter else: lines[t].p2 = circumcenter else: if not (triangle == 'obtuse' and t == s): lines[t].p2 = circumcenter else: lines[t].p1 = circumcenter t = t+1 #now determine the longest side of right triangle if triangle == 'right': t = dis[2][0] if Line.intersect(Line(lines[t].p1,circumcenter),tmp_lines[0][2]) != None or Line.intersect(Line(lines[t].p1,circumcenter),tmp_lines[1][2]) != None: lines[t].p1,lines[t].p2 = circumcenter,lines[t].p2 else: lines[t].p1,lines[t].p2 = circumcenter,lines[t].p1 #create circumcenter related points circumcenter.iscircumcenter = True lines[0].connected.append(lines[1]) lines[0].connected.append(lines[2]) lines[1].connected.append(lines[0]) lines[1].connected.append(lines[2]) lines[2].connected.append(lines[0]) lines[2].connected.append(lines[1]) else: mid.sort(key = lambda s: attrgetter('x','y')(s[0])) t = mid[1][1] del_line = lines[t] for i in range(lower,upper+1): u = self.points[i].related for j in range(0,len(u)): if u[j].line is del_line: del u[j] break del lines[t] #Line.intersect_with_edge(lines,Canvas.edge_painter,'colinear') return lines lines = []+clip() return VD(lines,range_points,self) elif (range_points[1]-range_points[0]+1) == 1: return VD([],range_points,self) else: mid = int((range_points[1]+range_points[0])/2) VDL = self.Voronoi((range_points[0],mid)) if self.isstep_by_step == True: #vdl = VD(copy.deepcopy(VDL.lines),copy.deepcopy(VDL.range_points),self) vdl = savevd(VDL.lines,VDL.range_points,self,VDL.convex) vdl.pos = 'left' vdl.color = QtCore.Qt.blue self.vd[0].append(vdl) VDR = self.Voronoi((mid+1,range_points[1])) if self.isstep_by_step == True: #vdr = VD(copy.deepcopy(VDR.lines),copy.deepcopy(VDR.range_points),self) vdr = savevd(VDR.lines,VDR.range_points,self,VDR.convex) vdr.pos = 'right' vdr.color = QtCore.Qt.red vdr.other = vdl vdl.other = vdr self.vd[0].append(vdr) merge_vd = VD.merge(VDL,VDR,self.tangent) self.debug_message[0].append(self.msg) self.msg = "" return merge_vd
# cv2.drawContours(img, [box], 0, (0, 255,0), 2) # Fit line trough the shape # img: width, height # https://docs.opencv.org/3.1.0/d3/dc0/group__imgproc__shape.html#gaf849da1fdafa67ee84b1e9a23b93f91f # Output line parameters. In case of 2D fitting, it should be a vector of 4 elements (like Vec4f) - (vx, vy, x0, y0), # where (vx, vy) is a normalized vector collinear to the line and (x0, y0) is a point on the line. [vx, vy, x, y] = cv2.fitLine(c, cv2.DIST_L2, 0, 0.01, 0.01) lefty = int((-x * vy / vx) + y) righty = int(((cols - x) * vy / vx) + y) point1 = (cols - 1, righty) point2 = (0, lefty) # cv2.line(img, point1, point2, (0, 255, 0), 2) newline = Line((cX, cY), point1, point2, img_center) lines.append(newline) # plt.plot([cols - 1, righty], [0, lefty], color='k', linestyle='-', linewidth=2) # break lines.sort(key=lambda l: l.angle) bestline = Line((int(cols / 2), int(rows / 2)), (0, int(rows / 2)), (cols, int(rows / 2)), img_center) # Initial best line is worst ever, 90 degrees with flight path # find all good lines (closest to Y axis) goodlines = [] for l in lines: if l.angle < abs(bestline.angle): goodlines.append(l)
def merge(VDL,VDR,tangent): clip_lines = [] #used to record ray which intersect with dividing chain #using hash table ray_list = set() def discard_edges(ray,circumcenter,side,SG_bisector): def recursive_discard_edge(ray,other_point,base_point,side): #want to delete left remaining line for candidate in ray.connected: if candidate.avail == True and candidate not in ray_list: next_base_point = None next_other_point = None #catch base point if(candidate.p1 is base_point or candidate.p2 is base_point): if candidate.p1 is base_point: next_base_point = candidate.p2 next_other_point = candidate.p1 else: next_base_point = candidate.p1 next_other_point = candidate.p2 if side == 'right': if ConvexHull.cross(base_point,next_base_point,other_point) > 0: candidate.avail = False recursive_discard_edge(candidate,next_other_point,next_base_point,'right') elif side == 'left': if ConvexHull.cross(base_point,next_base_point,other_point) < 0: candidate.avail = False recursive_discard_edge(candidate,next_other_point,next_base_point,'left') if side == 'right': #clear the edges extend to the left of HP #Line(hole,ray.p1) or Line(hole,ray.p2) must cw to Line(hole,bisector.p1) if ConvexHull.cross(circumcenter,ray.p1,SG_bisector.p1)>0: #this means p1 is left to circumcenter,so replace p1 with circumcenter if ray.p1.iscircumcenter == True: recursive_discard_edge(ray,circumcenter,ray.p1,'right') ray.p1 = circumcenter else: if ray.p2.iscircumcenter == True: recursive_discard_edge(ray,circumcenter,ray.p2,'right') ray.p2 = circumcenter elif side == "left": #clear the edges extend to the right of HP #Line(hole,ray.p1) or Line(hole,ray.p2) must ccw to Line(hole,bisector.p1) if ConvexHull.cross(circumcenter,ray.p1,SG_bisector.p1)<0: #this means p1 is right to circumcenter,so replace p1 with circumcenter if ray.p1.iscircumcenter == True: recursive_discard_edge(ray,circumcenter,ray.p1,'left') ray.p1 = circumcenter else: if ray.p2.iscircumcenter == True: recursive_discard_edge(ray,circumcenter,ray.p2,'left') ray.p2 = circumcenter else: #clear both side #clear the edges extend to the right of HP #Line(hole,ray.p1) or Line(hole,ray.p2) must ccw to Line(hole,bisector.p1) if ConvexHull.cross(circumcenter,ray[0].p1,SG_bisector.p1)<0: #this means p1 is right to circumcenter,so replace p1 with circumcenter if ray[0].p1.iscircumcenter == True: recursive_discard_edge(ray[0],circumcenter,ray[0].p1,'left') ray[0].p1 = circumcenter else: if ray[0].p2.iscircumcenter == True: recursive_discard_edge(ray[0],circumcenter,ray[0].p2,'left') ray[0].p2 = circumcenter #clear the edges extend to the left of HP if ConvexHull.cross(circumcenter,ray[1].p1,SG_bisector.p1)>0: #this means p1 is left to circumcenter,so replace p1 with circumcenter if ray[1].p1.iscircumcenter == True: recursive_discard_edge(ray[1],circumcenter,ray[1].p1,'right') ray[1].p1 = circumcenter else: if ray[1].p2.iscircumcenter == True: recursive_discard_edge(ray[1],circumcenter,ray[1].p2,'right') ray[1].p2 = circumcenter def nextPoint(pool,SG_bisector): ans = None first = True for candidate in pool: if candidate.line.avail== True and SG_bisector.p1 is not candidate.line.hole: result = Line.intersect(candidate.line,SG_bisector) if result is not None: t = (result,candidate.point,candidate.line) if first == True: ans = t first = False else: if t[0].y <= ans[0].y: ans = t return ans upper_tangent,lower_tangent = VD.find_tangent(VDL,VDR) ul = (upper_tangent,lower_tangent) tangent[0].append(ul) HP = [] SG = upper_tangent px = SG.p1 py = SG.p2 #p1 of upper_tangent belongs to VDL, and p2 belongs to VDR SG_bisector = Line.biSector(SG.p1,SG.p2) SG_bisector._p1 = SG.p1 SG_bisector._p2 = SG.p2 HP.append(SG_bisector) circumcenter = None firsttime = True newpl = defaultdict(list) while not (SG == lower_tangent): #step4 as textBook #p1 of SG_bisector is fixed to upper position than p2 if SG_bisector.p1.y > SG_bisector.p2.y: SG_bisector.p1,SG_bisector.p2 = SG_bisector.p2,SG_bisector.p1 elif abs((SG_bisector.p1.y)-(SG_bisector.p2.y)) <= 0.00005: if SG_bisector.p1.x<SG_bisector.p2.x: SG_bisector.p1,SG_bisector.p2 = SG_bisector.p2,SG_bisector.p1 newpl[SG.p1].append(pair(SG_bisector,SG.p2)) newpl[SG.p2].append(pair(SG_bisector,SG.p1)) #orginally p1 is very far from painter,so we need to fix p1 to previous circumcenter if firsttime == False and circumcenter is not None: SG_bisector.p1 = circumcenter pll = px.related prl = py.related result_l = nextPoint(pll,SG_bisector) result_r = nextPoint(prl,SG_bisector) side = None ray = None #with biSector of pyz2 first,that is,VDR first if result_l is not None and result_r is not None: if abs(result_l[0].x-result_r[0].x) <= 0.05 and abs(result_l[0].y-result_r[0].y) <= 0.05: #VDL.parent.msg = VDL.parent.msg+'both cd'+'\n' SG = Line(result_l[1],result_r[1]); circumcenter = result_l[0] ray = (result_l[2],result_r[2]) side = 'both' elif result_l[0].y > result_r[0].y: #VDL.parent.msg = VDL.parent.msg+'cd VDR'+'\n' SG = Line(px,result_r[1]) circumcenter = result_r[0] ray = result_r[2] side = 'right' elif result_l[0].y < result_r[0].y: #VDL.parent.msg = VDL.parent.msg+'cd VDL'+'\n' SG = Line(result_l[1],py) circumcenter = result_l[0] ray = result_l[2] side = 'left' else: print 'confused...' #print result_l,result_r else: if result_l is not None and result_r is None: #VDL.parent.msg = VDL.parent.msg+'VDR is None,cd VDL'+'\n' SG = Line(result_l[1],py) circumcenter = result_l[0] ray = result_l[2] side = 'left' elif result_r is not None and result_l is None: #VDL.parent.msg = VDL.parent.msg+'VDL is None,cd VDR'+'\n' SG = Line(px,result_r[1]) circumcenter = result_r[0] #print 'circumcenter',(circumcenter.x,circumcenter.y) ray = result_r[2] side = 'right' else: #VDL.parent.msg = VDL.parent.msg+'both are None'+'\n' #let SG be lower_tangent SG = lower_tangent SG_bisector = Line.biSector(SG.p1,SG.p2) SG_bisector._p1 = SG.p1 SG_bisector._p2 = SG.p2 HP.append(SG_bisector) continue if ray is not None: if not isinstance(ray,tuple): ray.hole = circumcenter t = (ray,SG_bisector,side,circumcenter) if ray not in ray_list: ray_list.add(ray) clip_lines.append(t) else: for r in ray: r.hole = circumcenter ray_list.add(r) t = (ray,SG_bisector,side,circumcenter) clip_lines.append(t) if circumcenter is not None: circumcenter.iscircumcenter = True #lower point is replaced by circumcenter SG_bisector.p2 = circumcenter #new SG px = SG.p1 py = SG.p2 SG_bisector = Line.biSector(SG.p1,SG.p2) SG_bisector._p1 = SG.p1 SG_bisector._p2 = SG.p2 HP.append(SG_bisector) firsttime = False #the end of while loop for HP if SG_bisector.p1.y > SG_bisector.p2.y: SG_bisector.p1,SG_bisector.p2 = SG_bisector.p2,SG_bisector.p1 elif abs((SG_bisector.p1.y)-(SG_bisector.p2.y)) <= 0.00005: if SG_bisector.p1.x<SG_bisector.p2.x: SG_bisector.p1,SG_bisector.p2 = SG_bisector.p2,SG_bisector.p1 newpl[SG.p1].append(pair(SG_bisector,SG.p2)) newpl[SG.p2].append(pair(SG_bisector,SG.p1)) for p in newpl.keys(): for t in newpl[p]: p.related.append(t) if circumcenter is not None: SG_bisector.p1 = circumcenter #clip the unwanted lines VDL.parent.msg = VDL.parent.msg+ 'clip lines'+'\n' for t in clip_lines: ray = t[0] circumcenter = t[3] SG_bisector = t[1] side = t[2] discard_edges(ray,circumcenter,side,SG_bisector) #add new connected line s = 0 for t in range(0,len(HP)-1): #need to add the intersected dividing chain HP[t].connected.append(HP[t+1]) HP[t+1].connected.append(HP[t]) #need to add the intersected ray if s != len(clip_lines): if not isinstance(clip_lines[s][0],tuple): HP[t].connected.append(clip_lines[s][0]) clip_lines[s][0].connected.append(HP[t]) HP[t+1].connected.append(clip_lines[s][0]) clip_lines[s][0].connected.append(HP[t+1]) else: r = clip_lines[s][0] HP[t].connected.append(r[0]) r[0].connected.append(HP[t]) HP[t+1].connected.append(r[0]) r[0].connected.append(HP[t+1]) HP[t].connected.append(r[1]) r[1].connected.append(HP[t]) HP[t+1].connected.append(r[1]) r[1].connected.append(HP[t+1]) r[1].connected.append(r[0]) r[0].connected.append(r[1]) s = s+1 lines = [] #lines = VDR.lines+VDL.lines+HP lines.append(VDR.lines) lines.append(VDL.lines) lines.append(HP) if VDL.parent.isstep_by_step == True: hp = [] for h in HP: hp.append(Line(Point(h.p1.x,h.p1.y),Point(h.p2.x,h.p2.y))) VDR.parent.hp[0].append(hp) #VDR.parent.hp[0].append(copy.deepcopy(HP)) range_points = (VDL.range_points[0],VDR.range_points[1]) return VD(lines,range_points,VDR.parent)
def merge(VDL, VDR, tangent): clip_lines = [] #used to record ray which intersect with dividing chain #using hash table ray_list = set() def discard_edges(ray, circumcenter, side, SG_bisector): def recursive_discard_edge(ray, other_point, base_point, side): #want to delete left remaining line for candidate in ray.connected: if candidate.avail == True and candidate not in ray_list: next_base_point = None next_other_point = None #catch base point if (candidate.p1 is base_point or candidate.p2 is base_point): if candidate.p1 is base_point: next_base_point = candidate.p2 next_other_point = candidate.p1 else: next_base_point = candidate.p1 next_other_point = candidate.p2 if side == 'right': if ConvexHull.cross(base_point, next_base_point, other_point) > 0: candidate.avail = False recursive_discard_edge( candidate, next_other_point, next_base_point, 'right') elif side == 'left': if ConvexHull.cross(base_point, next_base_point, other_point) < 0: candidate.avail = False recursive_discard_edge( candidate, next_other_point, next_base_point, 'left') if side == 'right': #clear the edges extend to the left of HP #Line(hole,ray.p1) or Line(hole,ray.p2) must cw to Line(hole,bisector.p1) if ConvexHull.cross(circumcenter, ray.p1, SG_bisector.p1) > 0: #this means p1 is left to circumcenter,so replace p1 with circumcenter if ray.p1.iscircumcenter == True: recursive_discard_edge(ray, circumcenter, ray.p1, 'right') ray.p1 = circumcenter else: if ray.p2.iscircumcenter == True: recursive_discard_edge(ray, circumcenter, ray.p2, 'right') ray.p2 = circumcenter elif side == "left": #clear the edges extend to the right of HP #Line(hole,ray.p1) or Line(hole,ray.p2) must ccw to Line(hole,bisector.p1) if ConvexHull.cross(circumcenter, ray.p1, SG_bisector.p1) < 0: #this means p1 is right to circumcenter,so replace p1 with circumcenter if ray.p1.iscircumcenter == True: recursive_discard_edge(ray, circumcenter, ray.p1, 'left') ray.p1 = circumcenter else: if ray.p2.iscircumcenter == True: recursive_discard_edge(ray, circumcenter, ray.p2, 'left') ray.p2 = circumcenter else: #clear both side #clear the edges extend to the right of HP #Line(hole,ray.p1) or Line(hole,ray.p2) must ccw to Line(hole,bisector.p1) if ConvexHull.cross(circumcenter, ray[0].p1, SG_bisector.p1) < 0: #this means p1 is right to circumcenter,so replace p1 with circumcenter if ray[0].p1.iscircumcenter == True: recursive_discard_edge(ray[0], circumcenter, ray[0].p1, 'left') ray[0].p1 = circumcenter else: if ray[0].p2.iscircumcenter == True: recursive_discard_edge(ray[0], circumcenter, ray[0].p2, 'left') ray[0].p2 = circumcenter #clear the edges extend to the left of HP if ConvexHull.cross(circumcenter, ray[1].p1, SG_bisector.p1) > 0: #this means p1 is left to circumcenter,so replace p1 with circumcenter if ray[1].p1.iscircumcenter == True: recursive_discard_edge(ray[1], circumcenter, ray[1].p1, 'right') ray[1].p1 = circumcenter else: if ray[1].p2.iscircumcenter == True: recursive_discard_edge(ray[1], circumcenter, ray[1].p2, 'right') ray[1].p2 = circumcenter def nextPoint(pool, SG_bisector): ans = None first = True for candidate in pool: if candidate.line.avail == True and SG_bisector.p1 is not candidate.line.hole: result = Line.intersect(candidate.line, SG_bisector) if result is not None: t = (result, candidate.point, candidate.line) if first == True: ans = t first = False else: if t[0].y <= ans[0].y: ans = t return ans upper_tangent, lower_tangent = VD.find_tangent(VDL, VDR) ul = (upper_tangent, lower_tangent) tangent[0].append(ul) HP = [] SG = upper_tangent px = SG.p1 py = SG.p2 #p1 of upper_tangent belongs to VDL, and p2 belongs to VDR SG_bisector = Line.biSector(SG.p1, SG.p2) SG_bisector._p1 = SG.p1 SG_bisector._p2 = SG.p2 HP.append(SG_bisector) circumcenter = None firsttime = True newpl = defaultdict(list) while not (SG == lower_tangent): #step4 as textBook #p1 of SG_bisector is fixed to upper position than p2 if SG_bisector.p1.y > SG_bisector.p2.y: SG_bisector.p1, SG_bisector.p2 = SG_bisector.p2, SG_bisector.p1 elif abs((SG_bisector.p1.y) - (SG_bisector.p2.y)) <= 0.00005: if SG_bisector.p1.x < SG_bisector.p2.x: SG_bisector.p1, SG_bisector.p2 = SG_bisector.p2, SG_bisector.p1 newpl[SG.p1].append(pair(SG_bisector, SG.p2)) newpl[SG.p2].append(pair(SG_bisector, SG.p1)) #orginally p1 is very far from painter,so we need to fix p1 to previous circumcenter if firsttime == False and circumcenter is not None: SG_bisector.p1 = circumcenter pll = px.related prl = py.related result_l = nextPoint(pll, SG_bisector) result_r = nextPoint(prl, SG_bisector) side = None ray = None #with biSector of pyz2 first,that is,VDR first if result_l is not None and result_r is not None: if abs(result_l[0].x - result_r[0].x) <= 0.05 and abs( result_l[0].y - result_r[0].y) <= 0.05: #VDL.parent.msg = VDL.parent.msg+'both cd'+'\n' SG = Line(result_l[1], result_r[1]) circumcenter = result_l[0] ray = (result_l[2], result_r[2]) side = 'both' elif result_l[0].y > result_r[0].y: #VDL.parent.msg = VDL.parent.msg+'cd VDR'+'\n' SG = Line(px, result_r[1]) circumcenter = result_r[0] ray = result_r[2] side = 'right' elif result_l[0].y < result_r[0].y: #VDL.parent.msg = VDL.parent.msg+'cd VDL'+'\n' SG = Line(result_l[1], py) circumcenter = result_l[0] ray = result_l[2] side = 'left' else: print('confused...') #print result_l,result_r else: if result_l is not None and result_r is None: #VDL.parent.msg = VDL.parent.msg+'VDR is None,cd VDL'+'\n' SG = Line(result_l[1], py) circumcenter = result_l[0] ray = result_l[2] side = 'left' elif result_r is not None and result_l is None: #VDL.parent.msg = VDL.parent.msg+'VDL is None,cd VDR'+'\n' SG = Line(px, result_r[1]) circumcenter = result_r[0] #print 'circumcenter',(circumcenter.x,circumcenter.y) ray = result_r[2] side = 'right' else: #VDL.parent.msg = VDL.parent.msg+'both are None'+'\n' #let SG be lower_tangent SG = lower_tangent SG_bisector = Line.biSector(SG.p1, SG.p2) SG_bisector._p1 = SG.p1 SG_bisector._p2 = SG.p2 HP.append(SG_bisector) continue if ray is not None: if not isinstance(ray, tuple): ray.hole = circumcenter t = (ray, SG_bisector, side, circumcenter) if ray not in ray_list: ray_list.add(ray) clip_lines.append(t) else: for r in ray: r.hole = circumcenter ray_list.add(r) t = (ray, SG_bisector, side, circumcenter) clip_lines.append(t) if circumcenter is not None: circumcenter.iscircumcenter = True #lower point is replaced by circumcenter SG_bisector.p2 = circumcenter #new SG px = SG.p1 py = SG.p2 SG_bisector = Line.biSector(SG.p1, SG.p2) SG_bisector._p1 = SG.p1 SG_bisector._p2 = SG.p2 HP.append(SG_bisector) firsttime = False #the end of while loop for HP if SG_bisector.p1.y > SG_bisector.p2.y: SG_bisector.p1, SG_bisector.p2 = SG_bisector.p2, SG_bisector.p1 elif abs((SG_bisector.p1.y) - (SG_bisector.p2.y)) <= 0.00005: if SG_bisector.p1.x < SG_bisector.p2.x: SG_bisector.p1, SG_bisector.p2 = SG_bisector.p2, SG_bisector.p1 newpl[SG.p1].append(pair(SG_bisector, SG.p2)) newpl[SG.p2].append(pair(SG_bisector, SG.p1)) for p in newpl.keys(): for t in newpl[p]: p.related.append(t) if circumcenter is not None: SG_bisector.p1 = circumcenter #clip the unwanted lines VDL.parent.msg = VDL.parent.msg + 'clip lines' + '\n' for t in clip_lines: ray = t[0] circumcenter = t[3] SG_bisector = t[1] side = t[2] discard_edges(ray, circumcenter, side, SG_bisector) #add new connected line s = 0 for t in range(0, len(HP) - 1): #need to add the intersected dividing chain HP[t].connected.append(HP[t + 1]) HP[t + 1].connected.append(HP[t]) #need to add the intersected ray if s != len(clip_lines): if not isinstance(clip_lines[s][0], tuple): HP[t].connected.append(clip_lines[s][0]) clip_lines[s][0].connected.append(HP[t]) HP[t + 1].connected.append(clip_lines[s][0]) clip_lines[s][0].connected.append(HP[t + 1]) else: r = clip_lines[s][0] HP[t].connected.append(r[0]) r[0].connected.append(HP[t]) HP[t + 1].connected.append(r[0]) r[0].connected.append(HP[t + 1]) HP[t].connected.append(r[1]) r[1].connected.append(HP[t]) HP[t + 1].connected.append(r[1]) r[1].connected.append(HP[t + 1]) r[1].connected.append(r[0]) r[0].connected.append(r[1]) s = s + 1 lines = [] #lines = VDR.lines+VDL.lines+HP lines.append(VDR.lines) lines.append(VDL.lines) lines.append(HP) if VDL.parent.isstep_by_step == True: hp = [] for h in HP: hp.append(Line(Point(h.p1.x, h.p1.y), Point(h.p2.x, h.p2.y))) VDR.parent.hp[0].append(hp) #VDR.parent.hp[0].append(copy.deepcopy(HP)) range_points = (VDL.range_points[0], VDR.range_points[1]) return VD(lines, range_points, VDR.parent)
class Canvas(QtWidgets.QWidget): edge_painter = (Line(Point(0,0),Point(610,0)),Line(Point(0,0),Point(0,610)),Line(Point(610,0),Point(610,610)),Line(Point(0,610),Point(610,610))) def __init__(self,parent = None): super(Canvas, self).__init__() self.IOData = IOData(self) self.drawDisplay = drawDisplay(self) self.parent = parent self.points = [] #used to output original points include duplicate self._points = [] self.lines = [] self.vd = [[],0] self.tangent = [[],0] self.hp = [[],0] self.isstep_by_step = False self.debug_message = [[],0] self.msg = "" self.vertex = [] self.setMouseTracking(True) def mousePressEvent(self,event): p = Point(event.x(),event.y()) self._points.append(p) if p not in self.points: self.points.append(p) self.drawDisplay.display_points(p) self.update() def mouseMoveEvent(self, event): self.parent.mouse_location.setText("("+event.x().__str__()+","+event.y().__str__()+")") def paintEvent(self, event): qp = QtGui.QPainter() qp.begin(self) #print 'paintEvent' self.drawDisplay.drawPoints(qp) self.drawDisplay.drawLines(qp) qp.end() def Voronoi(self,range_points): if (range_points[1]-range_points[0]+1) == 2: #print 'len = 2' lower = range_points[0] upper = range_points[1] line = [Line.biSector(self.points[lower],self.points[upper])] line[0]._p1 = self.points[lower] line[0]._p2 = self.points[upper] #hash table for point mapping to related biSector self.points[lower].related.append(pair(line[0],self.points[upper])) self.points[upper].related.append(pair(line[0],self.points[lower])) #Line.intersect_with_edge(line,Canvas.edge_painter,'colinear') vd = VD(line,range_points,self) return vd elif (range_points[1]-range_points[0]+1) == 3: #print 'len = 3' def clip(): lower = range_points[0] upper = range_points[1] lines = [] dis = [] t = 0 mid = [] for i in range(lower,upper): for j in range(i+1,upper+1): lines.append(Line.biSector(self.points[i],self.points[j])) self.points[i].related.append(pair(lines[-1],self.points[j])) self.points[j].related.append(pair(lines[-1],self.points[i])) lines[-1]._p1 = self.points[i] lines[-1]._p2 = self.points[j] mid.append(((self.points[i]+self.points[j])/2,t)) dis.append((t,(self.points[i].x-self.points[j].x)**2+(self.points[i].y-self.points[j].y)**2,Line(self.points[i],self.points[j]))) t = t+1 circumcenter = Line.intersect(lines[0],lines[1]) if circumcenter is not None: tmp_lines = None dis.sort(key = lambda x : x[1]) triangle = 'acute' if dis[0][1]+dis[1][1] == dis[2][1]: triangle = 'right' tmp_lines = dis[0:2] elif dis[0][1]+dis[1][1]<dis[2][1]: triangle = 'obtuse' #print triangle s = dis[2][0] t = 0 for i in range(lower,upper): for j in range(i+1,upper+1): ab = (Line(self.points[i],self.points[j])) hl = Line(lines[t].p1,circumcenter) result = Line.intersect(hl,ab) #do not determine the longest side of right triangle in the same way if not (triangle == 'right' and t == s): if result is None: #reverse policy for longest side of obtuse if not (triangle == 'obtuse' and t == s): lines[t].p1 = circumcenter else: lines[t].p2 = circumcenter else: if not (triangle == 'obtuse' and t == s): lines[t].p2 = circumcenter else: lines[t].p1 = circumcenter t = t+1 #now determine the longest side of right triangle if triangle == 'right': t = dis[2][0] if Line.intersect(Line(lines[t].p1,circumcenter),tmp_lines[0][2]) != None or Line.intersect(Line(lines[t].p1,circumcenter),tmp_lines[1][2]) != None: lines[t].p1,lines[t].p2 = circumcenter,lines[t].p2 else: lines[t].p1,lines[t].p2 = circumcenter,lines[t].p1 #create circumcenter related points circumcenter.iscircumcenter = True lines[0].connected.append(lines[1]) lines[0].connected.append(lines[2]) lines[1].connected.append(lines[0]) lines[1].connected.append(lines[2]) lines[2].connected.append(lines[0]) lines[2].connected.append(lines[1]) else: mid.sort(key = lambda s: attrgetter('x','y')(s[0])) t = mid[1][1] del_line = lines[t] for i in range(lower,upper+1): u = self.points[i].related for j in range(0,len(u)): if u[j].line is del_line: del u[j] break del lines[t] #Line.intersect_with_edge(lines,Canvas.edge_painter,'colinear') return lines lines = []+clip() return VD(lines,range_points,self) elif (range_points[1]-range_points[0]+1) == 1: return VD([],range_points,self) else: mid = int((range_points[1]+range_points[0])/2) VDL = self.Voronoi((range_points[0],mid)) if self.isstep_by_step == True: #vdl = VD(copy.deepcopy(VDL.lines),copy.deepcopy(VDL.range_points),self) vdl = savevd(VDL.lines,VDL.range_points,self,VDL.convex) vdl.pos = 'left' vdl.color = QtCore.Qt.blue self.vd[0].append(vdl) VDR = self.Voronoi((mid+1,range_points[1])) if self.isstep_by_step == True: #vdr = VD(copy.deepcopy(VDR.lines),copy.deepcopy(VDR.range_points),self) vdr = savevd(VDR.lines,VDR.range_points,self,VDR.convex) vdr.pos = 'right' vdr.color = QtCore.Qt.red vdr.other = vdl vdl.other = vdr self.vd[0].append(vdr) merge_vd = VD.merge(VDL,VDR,self.tangent) self.debug_message[0].append(self.msg) self.msg = "" return merge_vd def prepare(self): pset = set(self.points) self.points = [] for p in pset: self.points.append(p) self.points.sort(key= attrgetter('x','y')) def Run(self): if self.isstep_by_step == False: self.prepare() ans = self.Voronoi((0,len(self.points)-1)) Line.intersect_with_edge(ans.lines,Canvas.edge_painter) self.lines = ans.lines self.vertex = ans.convex.vertex self.drawDisplay.display_output() self.update() else: #current state is step_by_step, switch to run all vd while True: self.parent.step_button.setEnabled(False) self.repaint() if self.vd[1] == len(self.vd[0])-1: self.drawDisplay.display_output() time.sleep(1) self.vd[1] = self.vd[1]+1 if self.vd[1] >= len(self.vd[0]): break self.vd = [[],0] self.hp = [[],0] self.isstep_by_step = False def step_by_step(self): self.parent.step_button.setEnabled(True) if not self.isstep_by_step == True: self.isstep_by_step = True self.prepare() final = self.Voronoi((0,len(self.points)-1)) #for run after step self.lines = final.lines self.vertex = final.convex.vertex Line.intersect_with_edge(final.lines,Canvas.edge_painter) #final_vd = VD(copy.deepcopy(final.lines),copy.deepcopy(final.range_points),self) final_vd = savevd(final.lines,final.range_points,self,final.convex) final_vd.pos = 'final' final_vd.color = QtCore.Qt.black self.vd[0].append(final_vd) self.repaint() if self.vd[1] == len(self.vd[0])-1: self.drawDisplay.display_output() self.vd[1] = self.vd[1]+1 if self.vd[1] >= len(self.vd[0]): self.parent.step_button.setEnabled(False) self.vd = [[],0] self.hp = [[],0] self.isstep_by_step = False