class PolygonExtractor(object): CONTOUR = 1 MONOTONE = 2 def __init__(self, policy=MONOTONE): self.policy = policy self.hor_path_list = None self.ver_path_list = None self.merge_path_list = None def new_direction(self, direction): self.current_dir = direction self.all_path_list = [] self.curr_path_list = [] self.prev_line = [] self.curr_line = [] if DEBUG_POLYGONEXTRACTOR3: from pycam.Exporters.SVGExporter import SVGExporter self.svg = SVGExporter("test-%d.svg" % direction) if direction == 0: self.svg.fill("red") else: self.svg.fill("blue") if (self.policy == PolygonExtractor.CONTOUR) and (direction == 1) \ and self.hor_path_list: self.last_x = -INFINITE self.delta_x = 0 self.convert_hor_path_list() if DEBUG_POLYGONEXTRACTOR3: self.cont = SVGExporter("test-2.svg") self.cont.fill("green") def end_direction(self): if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: self.process_virtual_hor_scanline(INFINITE) self.new_scanline() self.end_scanline() if DEBUG_POLYGONEXTRACTOR3: self.svg.close() if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: for path in self.all_path_list: if DEBUG_POLYGONEXTRACTOR2: print("points=", path.points) i = 0 while i < len(path.points) - 1: if path.points[i][0] > path.points[i + 1][0]: if DEBUG_POLYGONEXTRACTOR2: print("drop point %d:" % path.points[i].id) path.points = path.points[:i] + path.points[i + 1:] if i > 0: i -= 1 else: i += 1 if DEBUG_POLYGONEXTRACTOR: print("%d paths" % len(self.all_path_list)) for path in self.all_path_list: print("%d:" % path.id), print("%d ->" % path.top_join.id) for point in path.points: print("%d(%g,%g)" % (point.id, point[0], point[1]), ) print("->%d" % path.bot_join.id) path_list = [] while len(self.all_path_list) > 0: p0 = self.all_path_list[0] for path in self.all_path_list: if path.id < p0.id: p0 = path if DEBUG_POLYGONEXTRACTOR: print("linking %d" % p0.id) self.all_path_list.remove(p0) p1 = p0.bot_join while True: if DEBUG_POLYGONEXTRACTOR: print("splice %d into %d" % (p1.id, p0.id)) self.all_path_list.remove(p1) p1.reverse() p0.points += p1.points if p1.top_join == p0: break p2 = p1.top_join if DEBUG_POLYGONEXTRACTOR: print("splicing %d into %d" % (p2.id, p0.id)) self.all_path_list.remove(p2) p0.points += p2.points p1 = p2.bot_join path_list.append(p0) if DEBUG_POLYGONEXTRACTOR: print("%d paths" % len(path_list)) for path in path_list: print("path %d(w=%d): " % (path.id, path.winding)), for point in path.points: print("%d(%g,%g)" % (point.id, point[0], point[1])), print() if self.current_dir == 0: self.hor_path_list = path_list elif self.current_dir == 1: if (self.policy == PolygonExtractor.CONTOUR ) and self.hor_path_list and path_list: self.merge_path_list = path_list else: self.ver_path_list = path_list if DEBUG_POLYGONEXTRACTOR3: from pycam.Exporters.SVGExporter import SVGExporter self.svg = SVGExporter("test-3.svg") for path in path_list: prev = None if len(path.points) <= 1: continue for p in path.points: if p.dir == 0: self.svg.fill("red") else: self.svg.fill("blue") self.svg.AddDot(p[0], p[1]) self.svg.AddText(p[0], p[1], str(p.id)) if prev: self.svg.AddLine(p[0], p[1], prev[0], prev[1]) prev = p p = path.points[0] self.svg.AddLine(p[0], p[1], prev[0], prev[1]) self.svg.close() self.cont.close() def finish(self): pass def new_scanline(self): self.curr_line = [] def append(self, p): if DEBUG_POLYGONEXTRACTOR3: p.dir = self.current_dir self.svg.AddDot(p[0], p[1]) self.svg.AddText(p[0], p[1], str(p.id)) self.curr_line.append(p) def end_scanline(self): if self.current_dir == 0: self.process_hor_scanline(self.curr_line) elif self.current_dir == 1: if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: next_x = -INFINITE if len(self.curr_line) > 0: next_x = self.curr_line[0][0] self.delta_x = next_x - self.last_x self.last_x = next_x else: next_x = self.last_x + self.delta_x if next_x > -INFINITE: self.process_virtual_hor_scanline(next_x) if DEBUG_POLYGONEXTRACTOR2: print("scanline =", self.curr_line) self.process_ver_scanline(self.curr_line) def process_hor_scanline(self, scanline): if DEBUG_POLYGONEXTRACTOR: last = 0 inside = False s = "" for point in scanline: next_x = point[0] if inside: s += "*" * int(next_x - last) else: s += " " * int(next_x - last) last = next_x inside = not inside print(s) if DEBUG_POLYGONEXTRACTOR: print("active paths: "), for path in self.curr_path_list: print("%d(%g,%g)" % (path.id, path.points[-1][0], path.points[-1][1])), print() print("prev points: "), for point in self.prev_line: print("(%g,%g)" % (point[0], point[1])), print() print("active points: "), for point in scanline: print("%d(%g,%g)" % (point.id, point[0], point[1])), print() prev_point = Iterator(self.prev_line) curr_point = Iterator(scanline) curr_path = Iterator(self.curr_path_list) winding = 0 while (prev_point.remains() > 0) or (curr_point.remains() > 0): if DEBUG_POLYGONEXTRACTOR: print("num_prev=%d, num_curr=%d" % (prev_point.remains(), curr_point.remains())) if (prev_point.remains() == 0) and (curr_point.remains() >= 2): c0 = next(curr_point) c1 = next(curr_point) # new path starts p0 = Path() p0.winding = winding + 1 if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g)" % (p0.id, c0[0], c0[1])) p0.append(c0) self.curr_path_list.append(p0) p1 = Path() p1.winding = winding if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g)" % (p1.id, c1[0], c1[1])) p1.append(c1) self.curr_path_list.append(p1) p0.top_join = p1 p1.top_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() == 0): # old path ends p0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % p0.id) self.all_path_list.append(p0) next(prev_point) p1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % p1.id) self.all_path_list.append(p1) next(prev_point) p0.bot_join = p1 p1.bot_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() >= 2): p0 = prev_point.peek(0) p1 = prev_point.peek(1) c0 = curr_point.peek(0) c1 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print("overlap test: p0=%g p1=%g" % (p0[0], p1[0])) print("overlap test: c0=%g c1=%g" % (c0[0], c1[0])) if c1[0] < p0[0]: # new segment is completely to the left # new path starts s0 = Path() if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g) w=%d" % (s0.id, c0[0], c0[0], winding + 1)) s0.append(c0) curr_path.insert(s0) s1 = Path() s0.winding = winding + 1 s1.winding = winding if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g) w=%d" % (s1.id, c1[0], c1[1], winding)) s1.append(c1) curr_path.insert(s1) next(curr_point) next(curr_point) s0.top_join = s1 s1.top_join = s0 elif c0[0] > p1[0]: # new segment is completely to the right # old path ends s0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % s0.id) self.all_path_list.append(s0) next(prev_point) s1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % s1.id) self.all_path_list.append(s1) next(prev_point) s0.bot_join = s1 s1.bot_join = s0 winding = s1.winding else: # new segment is overlapping left_path = next(curr_path) right_path = curr_path.peek() left_point = c0 right_point = c1 winding = left_path.winding next(curr_point) next(prev_point) overlap_p = True overlap_c = True while overlap_c or overlap_p: overlap_p = False overlap_c = False # check for path joins if prev_point.remains() >= 2: p2 = prev_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print("join test: p0=%g p1=%g p2=%g" % (p0[0], p1[0], p2[0])) print("join test: c0=%g c1=%g" % (c0[0], c1[0])) if p2[0] <= c1[0]: overlap_p = True if self.policy == PolygonExtractor.CONTOUR: s0 = curr_path.takeNext() s1 = curr_path.takeNext() if curr_path.remains() >= 1: right_path = curr_path.peek() self.all_path_list.append(s0) self.all_path_list.append(s1) if DEBUG_POLYGONEXTRACTOR: print("path %d joins %d" % (s0.id, s1.id)) s0.bot_join = s1 s1.bot_join = s0 elif self.policy == PolygonExtractor.MONOTONE: s0 = curr_path.takeNext() left_path.bot_join = s0 s0.bot_join = left_path if DEBUG_POLYGONEXTRACTOR: print("path %d joins %d" % (left_path.id, s0.id)) curr_path.remove(left_path) self.all_path_list.append(left_path) self.all_path_list.append(s0) s1 = next(curr_path) left_path = s1 right_path = curr_path.peek() next(prev_point) next(prev_point) winding = s1.winding p0 = p2 if prev_point.remains() >= 1: p1 = prev_point.peek(0) else: overlap_p = False # check for path splits if curr_point.remains() >= 2: c2 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print("split test: p0=%g p1=%g" % (p0[0], p1[0])) print("split test: c0=%g c1=%g c2=%g" % (c0[0], c1[0], c2[0])) if c2[0] <= p1[0]: overlap_c = True s0 = Path() s1 = Path() s0.winding = winding + 1 s1.winding = winding s0.top_join = s1 s1.top_join = s0 if DEBUG_POLYGONEXTRACTOR: print( "region split into %d and %d (w=%d)" % (s0.id, s1.id, winding + 1)) next(curr_point) c0 = next(curr_point) if self.policy == PolygonExtractor.CONTOUR: s0.append(c1) curr_path.insert(s0) s1.append(c2) curr_path.insert(s1) elif self.policy == PolygonExtractor.MONOTONE: s0.append(left_point) s1.append(c1) curr_path.insertBefore(s0) curr_path.insertBefore(s1) left_point = c2 if curr_point.remains() >= 1: c1 = curr_point.peek(0) right_point = c1 else: overlap_c = False if DEBUG_POLYGONEXTRACTOR: print("add to path %d(%g,%g)" % (left_path.id, left_point[0], left_point[1])) left_path.append(left_point) right_path.append(right_point) if right_path == curr_path.peek(): next(curr_path) if DEBUG_POLYGONEXTRACTOR: print("add to path %d(%g,%g)" % (right_path.id, right_point[0], right_point[1])) winding = right_path.winding next(prev_point) next(curr_point) if DEBUG_POLYGONEXTRACTOR: output = "active paths: " for path in self.curr_path_list: output += "%d(%g,%g,w=%d)" % (path.id, path.points[-1][0], path.points[-1][1], path.winding) print(output) self.prev_line = scanline def process_ver_scanline(self, scanline): if DEBUG_POLYGONEXTRACTOR3: prev = None for p in scanline: if p.dir == 0: self.cont.fill("red") else: self.cont.fill("blue") self.cont.AddDot(p[0], p[1]) self.cont.fill("black") self.cont.AddText(p[0], p[1], str(p.id)) if prev: self.cont.AddLine(prev[0], prev[1], p[0], p[1]) prev = p if DEBUG_POLYGONEXTRACTOR: last = 0 inside = False s = "" for point in scanline: next_y = point[1] if inside: s += "*" * int(next_y - last) else: s += " " * int(next_y - last) last = next_y inside = not inside print(s) if DEBUG_POLYGONEXTRACTOR: print("active paths: "), for path in self.curr_path_list: print("%d(%g,%g)" % (path.id, path.points[-1][0], path.points[-1][1])), print() print("prev points: "), for point in self.prev_line: print("(%g,%g)" % (point[0], point[1])), print() print("active points: "), for point in scanline: print("%d(%g,%g)" % (point.id, point[0], point[1])), print() prev_point = Iterator(self.prev_line) curr_point = Iterator(scanline) curr_path = Iterator(self.curr_path_list) winding = 0 while (prev_point.remains() > 0) or (curr_point.remains() > 0): if DEBUG_POLYGONEXTRACTOR: print("num_prev=%d, num_curr=%d" % (prev_point.remains(), curr_point.remains())) if (prev_point.remains() == 0) and (curr_point.remains() >= 2): c0 = next(curr_point) c1 = next(curr_point) # new path starts p0 = Path() p0.winding = winding + 1 if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g)" % (p0.id, c0[0], c0[1])) p0.append(c0) self.curr_path_list.append(p0) p1 = Path() p1.winding = winding if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g)" % (p1.id, c1[0], c1[1])) p1.append(c1) self.curr_path_list.append(p1) p0.top_join = p1 p1.top_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() == 0): # old path ends p0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % p0.id) self.all_path_list.append(p0) next(prev_point) p1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % p1.id) self.all_path_list.append(p1) next(prev_point) p0.bot_join = p1 p1.bot_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() >= 2): p0 = prev_point.peek(0) p1 = prev_point.peek(1) c0 = curr_point.peek(0) c1 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print("overlap test: p0=%g p1=%g" % (p0[0], p1[0])) print("overlap test: c0=%g c1=%g" % (c0[0], c1[0])) if c1[1] < p0[1]: # new segment is completely to the left # new path starts s0 = Path() if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g) w=%d" % (s0.id, c0[0], c0[1], winding + 1)) s0.append(c0) curr_path.insert(s0) s1 = Path() s0.winding = winding + 1 s1.winding = winding if DEBUG_POLYGONEXTRACTOR: print("new path %d(%g,%g) w=%d" % (s1.id, c1[0], c1[1], winding)) s1.append(c1) curr_path.insert(s1) next(curr_point) next(curr_point) s0.top_join = s1 s1.top_join = s0 elif c0[1] > p1[1]: # new segment is completely to the right # old path ends s0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % s0.id) self.all_path_list.append(s0) next(prev_point) s1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print("end path %d" % s1.id) self.all_path_list.append(s1) next(prev_point) s0.bot_join = s1 s1.bot_join = s0 winding = s1.winding else: # new segment is overlapping left_path = next(curr_path) right_path = curr_path.peek() left_point = c0 right_point = c1 winding = left_path.winding next(curr_point) next(prev_point) overlap_p = True overlap_c = True while overlap_c or overlap_p: overlap_p = False overlap_c = False # check for path joins if prev_point.remains() >= 2: p2 = prev_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print("join test: p0=%g p1=%g p2=%g" % (p0[0], p1[0], p2[0])) print("join test: c0=%g c1=%g" % (c0[0], c1[0])) if p2[1] <= c1[1]: overlap_p = True if self.policy == PolygonExtractor.CONTOUR: s0 = curr_path.takeNext() s1 = curr_path.takeNext() if curr_path.remains() >= 1: right_path = curr_path.peek() self.all_path_list.append(s0) self.all_path_list.append(s1) if DEBUG_POLYGONEXTRACTOR: print("path %d joins %d" % (s0.id, s1.id)) s0.bot_join = s1 s1.bot_join = s0 elif self.policy == PolygonExtractor.MONOTONE: s0 = curr_path.takeNext() left_path.bot_join = s0 s0.bot_join = left_path if DEBUG_POLYGONEXTRACTOR: print("path %d joins %d" % (left_path.id, s0.id)) curr_path.remove(left_path) self.all_path_list.append(left_path) self.all_path_list.append(s0) s1 = next(curr_path) left_path = s1 right_path = curr_path.peek() next(prev_point) next(prev_point) winding = s1.winding p0 = p2 if prev_point.remains() >= 1: p1 = prev_point.peek(0) else: overlap_p = False # check for path splits if curr_point.remains() >= 2: c2 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print("split test: p0=%g p1=%g" % (p0[0], p1[0])) print("split test: c0=%g c1=%g c2=%g" % (c0[0], c1[0], c2[0])) if c2[1] <= p1[1]: overlap_c = True s0 = Path() s1 = Path() s0.winding = winding + 1 s1.winding = winding s0.top_join = s1 s1.top_join = s0 if DEBUG_POLYGONEXTRACTOR: print( "region split into %d and %d (w=%d)" % (s0.id, s1.id, winding + 1)) next(curr_point) c0 = next(curr_point) if self.policy == PolygonExtractor.CONTOUR: s0.append(c1) curr_path.insert(s0) s1.append(c2) curr_path.insert(s1) elif self.policy == PolygonExtractor.MONOTONE: s0.append(left_point) s1.append(c1) curr_path.insertBefore(s0) curr_path.insertBefore(s1) left_point = c2 if curr_point.remains() >= 1: c1 = curr_point.peek(0) right_point = c1 else: overlap_c = False if DEBUG_POLYGONEXTRACTOR: print("add to path %d(%g,%g)" % (left_path.id, left_point[0], left_point[1])) left_path.append(left_point) right_path.append(right_point) if right_path == curr_path.peek(): next(curr_path) if DEBUG_POLYGONEXTRACTOR: print("add to path %d(%g,%g)" % (right_path.id, right_point[0], right_point[1])) winding = right_path.winding next(prev_point) next(curr_point) if DEBUG_POLYGONEXTRACTOR: output = "active paths: " for path in self.curr_path_list: output += "%d(%g,%g,w=%d)" % (path.id, path.points[-1][0], path.points[-1][1], path.winding) print(output) self.prev_line = scanline def convert_hor_path_list(self): if DEBUG_POLYGONEXTRACTOR2: print("converting hor paths") hor_path_list = [] for s in self.hor_path_list: allsame = True miny = s.points[0][1] maxy = s.points[0][1] for p in s.points: if not p[0] == s.points[0][0]: allsame = False if p[1] < miny: miny = p[1] if p[1] > maxy: maxy = p[1] if allsame: if DEBUG_POLYGONEXTRACTOR2: print("all same !") s0 = Path() for p in s.points: if p[1] == miny: s0.append(p) hor_path_list.append(s0) s1 = Path() for p in s.points: if p[1] == maxy: s1.append(p) hor_path_list.append(s1) continue prev = s.points[-1] p_iter = CyclicIterator(s.points) p = s.points[0] next_p = next(p_iter) while not ((prev[0] >= p[0]) and (next_p[0] > p[0])): p = next_p next_p = next(p_iter) count = 0 while count < len(s.points): s0 = Path() while next_p[0] >= p[0]: s0.append(p) p = next_p next_p = next(p_iter) count += 1 s0.append(p) while (len(s0.points) > 1) \ and (s0.points[0][0] == s0.points[1][0]): s0.points = s0.points[1:] while (len(s0.points) > 1) \ and (s0.points[-2][0] == s0.points[-1][0]): s0.points = s0.points[0:-1] hor_path_list.append(s0) s1 = Path() while next_p[0] <= p[0]: s1.append(p) p = next_p next_p = next(p_iter) count += 1 s1.append(p) s1.reverse() while (len(s1.points) > 1) \ and (s1.points[0][0] == s1.points[1][0]): s1.points = s1.points[1:] while (len(s1.points) > 1) \ and (s1.points[-2][0] == s1.points[-1][0]): s1.points = s1.points[:-1] hor_path_list.append(s1) hor_path_list.sort(key=lambda p: p.points[0][0]) if DEBUG_POLYGONEXTRACTOR2: print("ver_hor_path_list = ", hor_path_list) for s in hor_path_list: print("s%d =" % s.id), for point in s.points: print(point.id), print() self.ver_hor_path_list = hor_path_list self.act_hor_path_list = [] def process_virtual_hor_scanline(self, next_x): _next_x = next_x while next_x <= _next_x: next_x = INFINITE if self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0][0] < next_x): next_x = self.ver_hor_path_list[0].points[0][0] if self.act_hor_path_list \ and (self.act_hor_path_list[0].points[0][0] < next_x): next_x = self.act_hor_path_list[0].points[0][0] if next_x >= _next_x: return if DEBUG_POLYGONEXTRACTOR2: print("ver_hor_path_list =", self.ver_hor_path_list) print("act_hor_path_list =", self.act_hor_path_list) print("next_x =", next_x) if self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0][0] <= next_x): while self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0][0] <= next_x): self.act_hor_path_list.append(self.ver_hor_path_list[0]) self.ver_hor_path_list = self.ver_hor_path_list[1:] self.act_hor_path_list.sort(key=lambda p: p.points[0][0]) scanline = [] i = 0 while i < len(self.act_hor_path_list): s = self.act_hor_path_list[i] if DEBUG_POLYGONEXTRACTOR2: print("s =", s) scanline.append(s.points[0]) if s.points[0][0] <= next_x: if len(s.points) <= 1: if DEBUG_POLYGONEXTRACTOR2: print("remove list") self.act_hor_path_list.pop(i) continue else: if DEBUG_POLYGONEXTRACTOR2: print("remove point", s.points[0]) s.points.pop(0) i += 1 self.act_hor_path_list.sort(key=lambda p: p.points[0][0]) if not scanline: return scanline.sort(key=lambda l: l[1]) if DEBUG_POLYGONEXTRACTOR2: print("scanline' =", scanline) print("ver_hor_path_list =", self.ver_hor_path_list) print("act_hor_path_list =", self.act_hor_path_list) self.process_ver_scanline(scanline)
class PolygonExtractor(object): CONTOUR = 1 MONOTONE = 2 def __init__(self, policy=MONOTONE): self.policy = policy self.hor_path_list = None self.ver_path_list = None self.merge_path_list = None def new_direction(self, direction): self.current_dir = direction self.all_path_list = [] self.curr_path_list = [] self.prev_line = [] self.curr_line = [] if DEBUG_POLYGONEXTRACTOR3: self.svg = SVGExporter("test-%d.svg" % direction) if direction == 0: self.svg.fill("red") else: self.svg.fill("blue") if (self.policy == PolygonExtractor.CONTOUR) and (direction == 1) \ and self.hor_path_list: self.last_x = -INFINITE self.delta_x = 0 self.convert_hor_path_list() if DEBUG_POLYGONEXTRACTOR3: self.cont = SVGExporter("test-2.svg") self.cont.fill("green") def end_direction(self): if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: self.process_virtual_hor_scanline(INFINITE) self.new_scanline() self.end_scanline() if DEBUG_POLYGONEXTRACTOR3: self.svg.close() if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: for path in self.all_path_list: if DEBUG_POLYGONEXTRACTOR2: print "points=", path.points i = 0 while i < len(path.points)-1: if path.points[i].x > path.points[i+1].x: if DEBUG_POLYGONEXTRACTOR2: print "drop point %d:" % path.points[i].id path.points = path.points[:i] + path.points[i+1:] if i > 0: i -= 1 else: i += 1 if DEBUG_POLYGONEXTRACTOR: print "%d paths" % len(self.all_path_list) for path in self.all_path_list: print "%d:" % path.id, print "%d ->" % path.top_join.id for point in path.points: print "%d(%g,%g)" % (point.id, point.x, point.y), print "->%d" % path.bot_join.id path_list = [] while len(self.all_path_list) > 0: p0 = self.all_path_list[0] for path in self.all_path_list: if path.id < p0.id: p0 = path if DEBUG_POLYGONEXTRACTOR: print "linking %d" % p0.id self.all_path_list.remove(p0) p1 = p0.bot_join while True: if DEBUG_POLYGONEXTRACTOR: print "splice %d into %d" % (p1.id, p0.id) self.all_path_list.remove(p1) p1.reverse() p0.points += p1.points if p1.top_join == p0: break p2 = p1.top_join if DEBUG_POLYGONEXTRACTOR: print "splicing %d into %d" % (p2.id, p0.id) self.all_path_list.remove(p2) p0.points += p2.points p1 = p2.bot_join path_list.append(p0) if DEBUG_POLYGONEXTRACTOR: print "%d paths" % len(path_list) for path in path_list: print "path %d(w=%d): " % (path.id, path.winding), for point in path.points: print "%d(%g,%g)" % (point.id, point.x, point.y), print if self.current_dir == 0: self.hor_path_list = path_list elif self.current_dir == 1: if (self.policy == PolygonExtractor.CONTOUR) \ and self.hor_path_list and path_list: self.merge_path_list = path_list else: self.ver_path_list = path_list if DEBUG_POLYGONEXTRACTOR3: self.svg = SVGExporter("test-3.svg") for path in path_list: prev = None if len(path.points)<=1: continue for p in path.points: if p.dir == 0: self.svg.fill("red") else: self.svg.fill("blue") self.svg.AddDot(p.x, p.y) self.svg.AddText(p.x, p.y, str(p.id)) if prev: self.svg.AddLine(p.x, p.y, prev.x, prev.y) prev = p p = path.points[0] self.svg.AddLine(p.x, p.y, prev.x, prev.y) self.svg.close() self.cont.close() def finish(self): pass def new_scanline(self): self.curr_line = [] def append(self, p): if DEBUG_POLYGONEXTRACTOR3: p.dir = self.current_dir self.svg.AddDot(p.x, p.y) self.svg.AddText(p.x, p.y, str(p.id)) self.curr_line.append(p) def end_scanline(self): if self.current_dir == 0: self.process_hor_scanline(self.curr_line) elif self.current_dir == 1: if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: next_x = -INFINITE if len(self.curr_line) > 0: next_x = self.curr_line[0].x self.delta_x = next_x - self.last_x self.last_x = next_x else: next_x = self.last_x + self.delta_x if next_x > -INFINITE: self.process_virtual_hor_scanline(next_x) if DEBUG_POLYGONEXTRACTOR2: print "scanline =", self.curr_line self.process_ver_scanline(self.curr_line) def process_hor_scanline(self, scanline): if DEBUG_POLYGONEXTRACTOR: last = 0 inside = False s = "" for point in scanline: next_x = point.x if inside: s += "*" * int(next_x - last) else: s += " " * int(next_x - last) last = next_x inside = not inside print s if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g)" \ % (path.id, path.points[-1].x, path.points[-1].y), print print "prev points: ", for point in self.prev_line: print "(%g,%g)" % (point.x, point.y), print print "active points: ", for point in scanline: print "%d(%g,%g)" % (point.id, point.x, point.y), print prev_point = Iterator(self.prev_line) curr_point = Iterator(scanline) curr_path = Iterator(self.curr_path_list) winding = 0 while (prev_point.remains() > 0) or (curr_point.remains() > 0): if DEBUG_POLYGONEXTRACTOR: print "num_prev=%d, num_curr=%d" \ % (prev_point.remains(), curr_point.remains()) if (prev_point.remains() == 0) and (curr_point.remains() >= 2): c0 = curr_point.next() c1 = curr_point.next() # new path starts p0 = Path() p0.winding = winding + 1 if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p0.id, c0.x, c0.y) p0.append(c0) self.curr_path_list.append(p0) p1 = Path() p1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p1.id, c1.x, c1.y) p1.append(c1) self.curr_path_list.append(p1) p0.top_join = p1 p1.top_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() == 0): #old path ends p0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p0.id self.all_path_list.append(p0) prev_point.next() p1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p1.id self.all_path_list.append(p1) prev_point.next() p0.bot_join = p1 p1.bot_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() >= 2): p0 = prev_point.peek(0) p1 = prev_point.peek(1) c0 = curr_point.peek(0) c1 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "overlap test: p0=%g p1=%g" % (p0.x, p1.x) print "overlap test: c0=%g c1=%g" % (c0.x, c1.x) if c1.x < p0.x: # new segment is completely to the left # new path starts s0 = Path() if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s0.id, c0.x, c0.y, winding + 1) s0.append(c0) curr_path.insert(s0) s1 = Path() s0.winding = winding + 1 s1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s1.id, c1.x, c1.y, winding) s1.append(c1) curr_path.insert(s1) curr_point.next() curr_point.next() s0.top_join = s1 s1.top_join = s0 elif c0.x > p1.x: # new segment is completely to the right # old path ends s0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s0.id self.all_path_list.append(s0) prev_point.next() s1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s1.id self.all_path_list.append(s1) prev_point.next() s0.bot_join = s1 s1.bot_join = s0 winding = s1.winding else: # new segment is overlapping left_path = curr_path.next() right_path = curr_path.peek() left_point = c0 right_point = c1 winding = left_path.winding curr_point.next() prev_point.next() overlap_p = True overlap_c = True while overlap_c or overlap_p: overlap_p = False overlap_c = False # check for path joins if prev_point.remains()>=2: p2 = prev_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "join test: p0=%g p1=%g p2=%g" \ % (p0.x, p1.x, p2.x) print "join test: c0=%g c1=%g" % (c0.x, c1.x) if p2.x <= c1.x: overlap_p = True if self.policy == PolygonExtractor.CONTOUR: s0 = curr_path.takeNext() s1 = curr_path.takeNext() if curr_path.remains()>=1: right_path = curr_path.peek() self.all_path_list.append(s0) self.all_path_list.append(s1) if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (s0.id, s1.id) s0.bot_join = s1 s1.bot_join = s0 elif self.policy == PolygonExtractor.MONOTONE: s0 = curr_path.takeNext() left_path.bot_join = s0 s0.bot_join = left_path if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (left_path.id, s0.id) curr_path.remove(left_path) self.all_path_list.append(left_path) self.all_path_list.append(s0) s1 = curr_path.next() left_path = s1 right_path = curr_path.peek() prev_point.next() prev_point.next() winding = s1.winding p0 = p2 if prev_point.remains()>=1: p1 = prev_point.peek(0) else: overlap_p = False # check for path splits if curr_point.remains()>=2: c2 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "split test: p0=%g p1=%g" % (p0.x, p1.x) print "split test: c0=%g c1=%g c2=%g" \ % (c0.x, c1.x, c2.x) if c2.x <= p1.x: overlap_c = True s0 = Path() s1 = Path() s0.winding = winding + 1 s1.winding = winding s0.top_join = s1 s1.top_join = s0 if DEBUG_POLYGONEXTRACTOR: print "region split into %d and %d (w=%d)" \ % (s0.id, s1.id, winding + 1) curr_point.next() c0 = curr_point.next() if self.policy == PolygonExtractor.CONTOUR: s0.append(c1) curr_path.insert(s0) s1.append(c2) curr_path.insert(s1) elif self.policy == PolygonExtractor.MONOTONE: s0.append(left_point) s1.append(c1) curr_path.insertBefore(s0) curr_path.insertBefore(s1) left_point = c2 if curr_point.remains() >= 1: c1 = curr_point.peek(0) right_point = c1 else: overlap_c = False if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (left_path.id, left_point.x, left_point.y) left_path.append(left_point) right_path.append(right_point) if right_path == curr_path.peek(): curr_path.next() if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (right_path.id, right_point.x, right_point.y) winding = right_path.winding prev_point.next() curr_point.next() if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g,w=%d)" % (path.id, path.points[-1].x, path.points[-1].y, path.winding), print self.prev_line = scanline def process_ver_scanline(self, scanline): if DEBUG_POLYGONEXTRACTOR3: prev = None for p in scanline: if p.dir == 0: self.cont.fill("red") else: self.cont.fill("blue") self.cont.AddDot(p.x, p.y) self.cont.fill("black") self.cont.AddText(p.x, p.y, str(p.id)) if prev: self.cont.AddLine(prev.x, prev.y, p.x, p.y) prev = p if DEBUG_POLYGONEXTRACTOR: last = 0 inside = False s = "" for point in scanline: next_y = point.y if inside: s += "*" * int(next_y - last) else: s += " " * int(next_y - last) last = next_y inside = not inside print s if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g)" \ % (path.id, path.points[-1].x, path.points[-1].y), print print "prev points: ", for point in self.prev_line: print "(%g,%g)" % (point.x, point.y), print print "active points: ", for point in scanline: print "%d(%g,%g)" % (point.id, point.x, point.y), print prev_point = Iterator(self.prev_line) curr_point = Iterator(scanline) curr_path = Iterator(self.curr_path_list) winding = 0 while (prev_point.remains() > 0) or (curr_point.remains() > 0): if DEBUG_POLYGONEXTRACTOR: print "num_prev=%d, num_curr=%d" \ % (prev_point.remains(), curr_point.remains()) if (prev_point.remains() == 0) and (curr_point.remains() >= 2): c0 = curr_point.next() c1 = curr_point.next() # new path starts p0 = Path() p0.winding = winding + 1 if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p0.id, c0.x, c0.y) p0.append(c0) self.curr_path_list.append(p0) p1 = Path() p1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p1.id, c1.x, c1.y) p1.append(c1) self.curr_path_list.append(p1) p0.top_join = p1 p1.top_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() == 0): #old path ends p0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p0.id self.all_path_list.append(p0) prev_point.next() p1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p1.id self.all_path_list.append(p1) prev_point.next() p0.bot_join = p1 p1.bot_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() >= 2): p0 = prev_point.peek(0) p1 = prev_point.peek(1) c0 = curr_point.peek(0) c1 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "overlap test: p0=%g p1=%g" % (p0.x, p1.x) print "overlap test: c0=%g c1=%g" % (c0.x, c1.x) if c1.y < p0.y: # new segment is completely to the left # new path starts s0 = Path() if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s0.id, c0.x, c0.y, winding + 1) s0.append(c0) curr_path.insert(s0) s1 = Path() s0.winding = winding + 1 s1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s1.id, c1.x, c1.y, winding) s1.append(c1) curr_path.insert(s1) curr_point.next() curr_point.next() s0.top_join = s1 s1.top_join = s0 elif c0.y > p1.y: # new segment is completely to the right # old path ends s0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s0.id self.all_path_list.append(s0) prev_point.next() s1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s1.id self.all_path_list.append(s1) prev_point.next() s0.bot_join = s1 s1.bot_join = s0 winding = s1.winding else: # new segment is overlapping left_path = curr_path.next() right_path = curr_path.peek() left_point = c0 right_point = c1 winding = left_path.winding curr_point.next() prev_point.next() overlap_p = True overlap_c = True while overlap_c or overlap_p: overlap_p = False overlap_c = False # check for path joins if prev_point.remains() >= 2: p2 = prev_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "join test: p0=%g p1=%g p2=%g" \ % (p0.x, p1.x, p2.x) print "join test: c0=%g c1=%g" % (c0.x, c1.x) if p2.y <= c1.y: overlap_p = True if self.policy == PolygonExtractor.CONTOUR: s0 = curr_path.takeNext() s1 = curr_path.takeNext() if curr_path.remains() >= 1: right_path = curr_path.peek() self.all_path_list.append(s0) self.all_path_list.append(s1) if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (s0.id, s1.id) s0.bot_join = s1 s1.bot_join = s0 elif self.policy == PolygonExtractor.MONOTONE: s0 = curr_path.takeNext() left_path.bot_join = s0 s0.bot_join = left_path if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (left_path.id, s0.id) curr_path.remove(left_path) self.all_path_list.append(left_path) self.all_path_list.append(s0) s1 = curr_path.next() left_path = s1 right_path = curr_path.peek() prev_point.next() prev_point.next() winding = s1.winding p0 = p2 if prev_point.remains() >= 1: p1 = prev_point.peek(0) else: overlap_p = False # check for path splits if curr_point.remains()>=2: c2 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "split test: p0=%g p1=%g" % (p0.x, p1.x) print "split test: c0=%g c1=%g c2=%g" \ % (c0.x, c1.x, c2.x) if c2.y <= p1.y: overlap_c = True s0 = Path() s1 = Path() s0.winding = winding + 1 s1.winding = winding s0.top_join = s1 s1.top_join = s0 if DEBUG_POLYGONEXTRACTOR: print "region split into %d and %d (w=%d)" \ % (s0.id, s1.id, winding + 1) curr_point.next() c0 = curr_point.next() if self.policy == PolygonExtractor.CONTOUR: s0.append(c1) curr_path.insert(s0) s1.append(c2) curr_path.insert(s1) elif self.policy == PolygonExtractor.MONOTONE: s0.append(left_point) s1.append(c1) curr_path.insertBefore(s0) curr_path.insertBefore(s1) left_point = c2 if curr_point.remains() >= 1: c1 = curr_point.peek(0) right_point = c1 else: overlap_c = False if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (left_path.id, left_point.x, left_point.y) left_path.append(left_point) right_path.append(right_point) if right_path == curr_path.peek(): curr_path.next() if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (right_path.id, right_point.x, right_point.y) winding = right_path.winding prev_point.next() curr_point.next() if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g,w=%d)" % (path.id, path.points[-1].x, path.points[-1].y, path.winding), print self.prev_line = scanline def convert_hor_path_list(self): if DEBUG_POLYGONEXTRACTOR2: print "converting hor paths" hor_path_list = [] for s in self.hor_path_list: allsame = True miny = s.points[0].y maxy = s.points[0].y for p in s.points: if not p.x == s.points[0].x: allsame = False if p.y < miny: miny = p.y if p.y > maxy: maxy = p.y if allsame: if DEBUG_POLYGONEXTRACTOR2: print "all same !" s0 = Path() for p in s.points: if p.y == miny: s0.append(p) hor_path_list.append(s0) s1 = Path() for p in s.points: if p.y == maxy: s1.append(p) hor_path_list.append(s1) continue prev = s.points[-1] p_iter = CyclicIterator(s.points) p = s.points[0] next_p = p_iter.next() while not ((prev.x >= p.x) and (next_p.x > p.x)): p = next_p next_p = p_iter.next() count = 0 while count < len(s.points): s0 = Path() while next_p.x >= p.x: s0.append(p) p = next_p next_p = p_iter.next() count += 1 s0.append(p) while (len(s0.points) > 1) \ and (s0.points[0].x == s0.points[1].x): s0.points = s0.points[1:] while (len(s0.points) > 1) \ and (s0.points[-2].x == s0.points[-1].x): s0.points = s0.points[0:-1] hor_path_list.append(s0) s1 = Path() while next_p.x <= p.x: s1.append(p) p = next_p next_p = p_iter.next() count += 1 s1.append(p) s1.reverse() while (len(s1.points) > 1) \ and (s1.points[0].x == s1.points[1].x): s1.points = s1.points[1:] while (len(s1.points) > 1) \ and (s1.points[-2].x == s1.points[-1].x): s1.points = s1.points[:-1] hor_path_list.append(s1) hor_path_list.sort(cmp=lambda a, b: cmp(a.points[0].x, b.points[0].x)) if DEBUG_POLYGONEXTRACTOR2: print "ver_hor_path_list = ", hor_path_list for s in hor_path_list: print "s%d =" % s.id, for point in s.points: print point.id, print self.ver_hor_path_list = hor_path_list self.act_hor_path_list = [] def process_virtual_hor_scanline(self, next_x): _next_x = next_x while next_x <= _next_x: next_x = INFINITE if self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0].x < next_x): next_x = self.ver_hor_path_list[0].points[0].x if self.act_hor_path_list \ and (self.act_hor_path_list[0].points[0].x < next_x): next_x = self.act_hor_path_list[0].points[0].x if next_x >= _next_x: return if DEBUG_POLYGONEXTRACTOR2: print "ver_hor_path_list =", self.ver_hor_path_list print "act_hor_path_list =", self.act_hor_path_list print "next_x =", next_x if self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0].x <= next_x): while self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0].x <= next_x): self.act_hor_path_list.append(self.ver_hor_path_list[0]) self.ver_hor_path_list = self.ver_hor_path_list[1:] self.act_hor_path_list.sort(cmp=lambda a, b: cmp(a.points[0].x, b.points[0].x)) scanline = [] i = 0 while i < len(self.act_hor_path_list): s = self.act_hor_path_list[i] if DEBUG_POLYGONEXTRACTOR2: print "s =", s scanline.append(s.points[0]) if s.points[0].x <= next_x: if len(s.points) <= 1: if DEBUG_POLYGONEXTRACTOR2: print "remove list" self.act_hor_path_list = self.act_hor_path_list[:i] \ + self.act_hor_path_list[i+1:] # TODO: the variable "hor_list_removed" is never used. # Any idea? hor_list_removed = True continue else: if DEBUG_POLYGONEXTRACTOR2: print "remove point", s.points[0] s.points = s.points[1:] if len(s.points)> 0 and s.points[0].x == next_x: # TODO: the variable "repeat" is never used. # Any idea? repeat = True i += 1 self.act_hor_path_list.sort(cmp=lambda a, b: cmp(a.points[0].x, b.points[0].x)) if len(scanline) == 0: return scanline.sort(cmp=lambda a, b: cmp(a.y, b.y)) if DEBUG_POLYGONEXTRACTOR2: print "scanline' =", scanline print "ver_hor_path_list =", self.ver_hor_path_list print "act_hor_path_list =", self.act_hor_path_list self.process_ver_scanline(scanline)
class PolygonExtractor: CONTOUR = 1 MONOTONE = 2 def __init__(self, policy=MONOTONE): self.policy = policy self.hor_path_list = None self.ver_path_list = None self.merge_path_list = None def new_direction(self, direction): self.current_dir = direction self.all_path_list = [] self.curr_path_list = [] self.prev_line = [] self.curr_line = [] if DEBUG_POLYGONEXTRACTOR3: self.svg = SVGExporter("test-%d.svg" % direction) if direction == 0: self.svg.fill("red") else: self.svg.fill("blue") if (self.policy == PolygonExtractor.CONTOUR) and (direction == 1) \ and self.hor_path_list: self.last_x = -INFINITE self.delta_x = 0 self.convert_hor_path_list() if DEBUG_POLYGONEXTRACTOR3: self.cont = SVGExporter("test-2.svg") self.cont.fill("green") def end_direction(self): if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: self.process_virtual_hor_scanline(INFINITE) self.new_scanline() self.end_scanline() if DEBUG_POLYGONEXTRACTOR3: self.svg.close() if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: for path in self.all_path_list: if DEBUG_POLYGONEXTRACTOR2: print "points=", path.points i = 0 while i < len(path.points) - 1: if path.points[i].x > path.points[i + 1].x: if DEBUG_POLYGONEXTRACTOR2: print "drop point %d:" % path.points[i].id path.points = path.points[:i] + path.points[i + 1:] if i > 0: i -= 1 else: i += 1 if DEBUG_POLYGONEXTRACTOR: print "%d paths" % len(self.all_path_list) for path in self.all_path_list: print "%d:" % path.id, print "%d ->" % path.top_join.id for point in path.points: print "%d(%g,%g)" % (point.id, point.x, point.y), print "->%d" % path.bot_join.id path_list = [] while len(self.all_path_list) > 0: p0 = self.all_path_list[0] for path in self.all_path_list: if path.id < p0.id: p0 = path if DEBUG_POLYGONEXTRACTOR: print "linking %d" % p0.id self.all_path_list.remove(p0) p1 = p0.bot_join while True: if DEBUG_POLYGONEXTRACTOR: print "splice %d into %d" % (p1.id, p0.id) self.all_path_list.remove(p1) p1.reverse() p0.points += p1.points if p1.top_join == p0: break p2 = p1.top_join if DEBUG_POLYGONEXTRACTOR: print "splicing %d into %d" % (p2.id, p0.id) self.all_path_list.remove(p2) p0.points += p2.points p1 = p2.bot_join path_list.append(p0) if DEBUG_POLYGONEXTRACTOR: print "%d paths" % len(path_list) for path in path_list: print "path %d(w=%d): " % (path.id, path.winding), for point in path.points: print "%d(%g,%g)" % (point.id, point.x, point.y), print if self.current_dir == 0: self.hor_path_list = path_list elif self.current_dir == 1: if (self.policy == PolygonExtractor.CONTOUR) \ and self.hor_path_list and path_list: self.merge_path_list = path_list else: self.ver_path_list = path_list if DEBUG_POLYGONEXTRACTOR3: self.svg = SVGExporter("test-3.svg") for path in path_list: prev = None if len(path.points) <= 1: continue for p in path.points: if p.dir == 0: self.svg.fill("red") else: self.svg.fill("blue") self.svg.AddDot(p.x, p.y) self.svg.AddText(p.x, p.y, str(p.id)) if prev: self.svg.AddLine(p.x, p.y, prev.x, prev.y) prev = p p = path.points[0] self.svg.AddLine(p.x, p.y, prev.x, prev.y) self.svg.close() self.cont.close() def finish(self): pass def new_scanline(self): self.curr_line = [] def append(self, p): if DEBUG_POLYGONEXTRACTOR3: p.dir = self.current_dir self.svg.AddDot(p.x, p.y) self.svg.AddText(p.x, p.y, str(p.id)) self.curr_line.append(p) def end_scanline(self): if self.current_dir == 0: self.process_hor_scanline(self.curr_line) elif self.current_dir == 1: if self.policy == PolygonExtractor.CONTOUR and self.hor_path_list: next_x = -INFINITE if len(self.curr_line) > 0: next_x = self.curr_line[0].x self.delta_x = next_x - self.last_x self.last_x = next_x else: next_x = self.last_x + self.delta_x if next_x > -INFINITE: self.process_virtual_hor_scanline(next_x) if DEBUG_POLYGONEXTRACTOR2: print "scanline =", self.curr_line self.process_ver_scanline(self.curr_line) def process_hor_scanline(self, scanline): if DEBUG_POLYGONEXTRACTOR: last = 0 inside = False s = "" for point in scanline: next_x = point.x if inside: s += "*" * int(next_x - last) else: s += " " * int(next_x - last) last = next_x inside = not inside print s if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g)" \ % (path.id, path.points[-1].x, path.points[-1].y), print print "prev points: ", for point in self.prev_line: print "(%g,%g)" % (point.x, point.y), print print "active points: ", for point in scanline: print "%d(%g,%g)" % (point.id, point.x, point.y), print prev_point = Iterator(self.prev_line) curr_point = Iterator(scanline) curr_path = Iterator(self.curr_path_list) winding = 0 while (prev_point.remains() > 0) or (curr_point.remains() > 0): if DEBUG_POLYGONEXTRACTOR: print "num_prev=%d, num_curr=%d" \ % (prev_point.remains(), curr_point.remains()) if (prev_point.remains() == 0) and (curr_point.remains() >= 2): c0 = curr_point.next() c1 = curr_point.next() # new path starts p0 = Path() p0.winding = winding + 1 if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p0.id, c0.x, c0.y) p0.append(c0) self.curr_path_list.append(p0) p1 = Path() p1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p1.id, c1.x, c1.y) p1.append(c1) self.curr_path_list.append(p1) p0.top_join = p1 p1.top_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() == 0): #old path ends p0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p0.id self.all_path_list.append(p0) prev_point.next() p1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p1.id self.all_path_list.append(p1) prev_point.next() p0.bot_join = p1 p1.bot_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() >= 2): p0 = prev_point.peek(0) p1 = prev_point.peek(1) c0 = curr_point.peek(0) c1 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "overlap test: p0=%g p1=%g" % (p0.x, p1.x) print "overlap test: c0=%g c1=%g" % (c0.x, c1.x) if c1.x < p0.x: # new segment is completely to the left # new path starts s0 = Path() if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s0.id, c0.x, c0.y, winding + 1) s0.append(c0) curr_path.insert(s0) s1 = Path() s0.winding = winding + 1 s1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s1.id, c1.x, c1.y, winding) s1.append(c1) curr_path.insert(s1) curr_point.next() curr_point.next() s0.top_join = s1 s1.top_join = s0 elif c0.x > p1.x: # new segment is completely to the right # old path ends s0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s0.id self.all_path_list.append(s0) prev_point.next() s1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s1.id self.all_path_list.append(s1) prev_point.next() s0.bot_join = s1 s1.bot_join = s0 winding = s1.winding else: # new segment is overlapping left_path = curr_path.next() right_path = curr_path.peek() left_point = c0 right_point = c1 winding = left_path.winding curr_point.next() prev_point.next() overlap_p = True overlap_c = True while overlap_c or overlap_p: overlap_p = False overlap_c = False # check for path joins if prev_point.remains() >= 2: p2 = prev_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "join test: p0=%g p1=%g p2=%g" \ % (p0.x, p1.x, p2.x) print "join test: c0=%g c1=%g" % (c0.x, c1.x) if p2.x <= c1.x: overlap_p = True if self.policy == PolygonExtractor.CONTOUR: s0 = curr_path.takeNext() s1 = curr_path.takeNext() if curr_path.remains() >= 1: right_path = curr_path.peek() self.all_path_list.append(s0) self.all_path_list.append(s1) if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (s0.id, s1.id) s0.bot_join = s1 s1.bot_join = s0 elif self.policy == PolygonExtractor.MONOTONE: s0 = curr_path.takeNext() left_path.bot_join = s0 s0.bot_join = left_path if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (left_path.id, s0.id) curr_path.remove(left_path) self.all_path_list.append(left_path) self.all_path_list.append(s0) s1 = curr_path.next() left_path = s1 right_path = curr_path.peek() prev_point.next() prev_point.next() winding = s1.winding p0 = p2 if prev_point.remains() >= 1: p1 = prev_point.peek(0) else: overlap_p = False # check for path splits if curr_point.remains() >= 2: c2 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "split test: p0=%g p1=%g" % (p0.x, p1.x) print "split test: c0=%g c1=%g c2=%g" \ % (c0.x, c1.x, c2.x) if c2.x <= p1.x: overlap_c = True s0 = Path() s1 = Path() s0.winding = winding + 1 s1.winding = winding s0.top_join = s1 s1.top_join = s0 if DEBUG_POLYGONEXTRACTOR: print "region split into %d and %d (w=%d)" \ % (s0.id, s1.id, winding + 1) curr_point.next() c0 = curr_point.next() if self.policy == PolygonExtractor.CONTOUR: s0.append(c1) curr_path.insert(s0) s1.append(c2) curr_path.insert(s1) elif self.policy == PolygonExtractor.MONOTONE: s0.append(left_point) s1.append(c1) curr_path.insertBefore(s0) curr_path.insertBefore(s1) left_point = c2 if curr_point.remains() >= 1: c1 = curr_point.peek(0) right_point = c1 else: overlap_c = False if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (left_path.id, left_point.x, left_point.y) left_path.append(left_point) right_path.append(right_point) if right_path == curr_path.peek(): curr_path.next() if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (right_path.id, right_point.x, right_point.y) winding = right_path.winding prev_point.next() curr_point.next() if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g,w=%d)" % (path.id, path.points[-1].x, path.points[-1].y, path.winding), print self.prev_line = scanline def process_ver_scanline(self, scanline): if DEBUG_POLYGONEXTRACTOR3: prev = None for p in scanline: if p.dir == 0: self.cont.fill("red") else: self.cont.fill("blue") self.cont.AddDot(p.x, p.y) self.cont.fill("black") self.cont.AddText(p.x, p.y, str(p.id)) if prev: self.cont.AddLine(prev.x, prev.y, p.x, p.y) prev = p if DEBUG_POLYGONEXTRACTOR: last = 0 inside = False s = "" for point in scanline: next_y = point.y if inside: s += "*" * int(next_y - last) else: s += " " * int(next_y - last) last = next_y inside = not inside print s if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g)" \ % (path.id, path.points[-1].x, path.points[-1].y), print print "prev points: ", for point in self.prev_line: print "(%g,%g)" % (point.x, point.y), print print "active points: ", for point in scanline: print "%d(%g,%g)" % (point.id, point.x, point.y), print prev_point = Iterator(self.prev_line) curr_point = Iterator(scanline) curr_path = Iterator(self.curr_path_list) winding = 0 while (prev_point.remains() > 0) or (curr_point.remains() > 0): if DEBUG_POLYGONEXTRACTOR: print "num_prev=%d, num_curr=%d" \ % (prev_point.remains(), curr_point.remains()) if (prev_point.remains() == 0) and (curr_point.remains() >= 2): c0 = curr_point.next() c1 = curr_point.next() # new path starts p0 = Path() p0.winding = winding + 1 if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p0.id, c0.x, c0.y) p0.append(c0) self.curr_path_list.append(p0) p1 = Path() p1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g)" % (p1.id, c1.x, c1.y) p1.append(c1) self.curr_path_list.append(p1) p0.top_join = p1 p1.top_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() == 0): #old path ends p0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p0.id self.all_path_list.append(p0) prev_point.next() p1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % p1.id self.all_path_list.append(p1) prev_point.next() p0.bot_join = p1 p1.bot_join = p0 continue if (prev_point.remains() >= 2) and (curr_point.remains() >= 2): p0 = prev_point.peek(0) p1 = prev_point.peek(1) c0 = curr_point.peek(0) c1 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "overlap test: p0=%g p1=%g" % (p0.x, p1.x) print "overlap test: c0=%g c1=%g" % (c0.x, c1.x) if c1.y < p0.y: # new segment is completely to the left # new path starts s0 = Path() if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s0.id, c0.x, c0.y, winding + 1) s0.append(c0) curr_path.insert(s0) s1 = Path() s0.winding = winding + 1 s1.winding = winding if DEBUG_POLYGONEXTRACTOR: print "new path %d(%g,%g) w=%d" \ % (s1.id, c1.x, c1.y, winding) s1.append(c1) curr_path.insert(s1) curr_point.next() curr_point.next() s0.top_join = s1 s1.top_join = s0 elif c0.y > p1.y: # new segment is completely to the right # old path ends s0 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s0.id self.all_path_list.append(s0) prev_point.next() s1 = curr_path.takeNext() if DEBUG_POLYGONEXTRACTOR: print "end path %d" % s1.id self.all_path_list.append(s1) prev_point.next() s0.bot_join = s1 s1.bot_join = s0 winding = s1.winding else: # new segment is overlapping left_path = curr_path.next() right_path = curr_path.peek() left_point = c0 right_point = c1 winding = left_path.winding curr_point.next() prev_point.next() overlap_p = True overlap_c = True while overlap_c or overlap_p: overlap_p = False overlap_c = False # check for path joins if prev_point.remains() >= 2: p2 = prev_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "join test: p0=%g p1=%g p2=%g" \ % (p0.x, p1.x, p2.x) print "join test: c0=%g c1=%g" % (c0.x, c1.x) if p2.y <= c1.y: overlap_p = True if self.policy == PolygonExtractor.CONTOUR: s0 = curr_path.takeNext() s1 = curr_path.takeNext() if curr_path.remains() >= 1: right_path = curr_path.peek() self.all_path_list.append(s0) self.all_path_list.append(s1) if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (s0.id, s1.id) s0.bot_join = s1 s1.bot_join = s0 elif self.policy == PolygonExtractor.MONOTONE: s0 = curr_path.takeNext() left_path.bot_join = s0 s0.bot_join = left_path if DEBUG_POLYGONEXTRACTOR: print "path %d joins %d" \ % (left_path.id, s0.id) curr_path.remove(left_path) self.all_path_list.append(left_path) self.all_path_list.append(s0) s1 = curr_path.next() left_path = s1 right_path = curr_path.peek() prev_point.next() prev_point.next() winding = s1.winding p0 = p2 if prev_point.remains() >= 1: p1 = prev_point.peek(0) else: overlap_p = False # check for path splits if curr_point.remains() >= 2: c2 = curr_point.peek(1) if DEBUG_POLYGONEXTRACTOR: print "split test: p0=%g p1=%g" % (p0.x, p1.x) print "split test: c0=%g c1=%g c2=%g" \ % (c0.x, c1.x, c2.x) if c2.y <= p1.y: overlap_c = True s0 = Path() s1 = Path() s0.winding = winding + 1 s1.winding = winding s0.top_join = s1 s1.top_join = s0 if DEBUG_POLYGONEXTRACTOR: print "region split into %d and %d (w=%d)" \ % (s0.id, s1.id, winding + 1) curr_point.next() c0 = curr_point.next() if self.policy == PolygonExtractor.CONTOUR: s0.append(c1) curr_path.insert(s0) s1.append(c2) curr_path.insert(s1) elif self.policy == PolygonExtractor.MONOTONE: s0.append(left_point) s1.append(c1) curr_path.insertBefore(s0) curr_path.insertBefore(s1) left_point = c2 if curr_point.remains() >= 1: c1 = curr_point.peek(0) right_point = c1 else: overlap_c = False if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (left_path.id, left_point.x, left_point.y) left_path.append(left_point) right_path.append(right_point) if right_path == curr_path.peek(): curr_path.next() if DEBUG_POLYGONEXTRACTOR: print "add to path %d(%g,%g)" \ % (right_path.id, right_point.x, right_point.y) winding = right_path.winding prev_point.next() curr_point.next() if DEBUG_POLYGONEXTRACTOR: print "active paths: ", for path in self.curr_path_list: print "%d(%g,%g,w=%d)" % (path.id, path.points[-1].x, path.points[-1].y, path.winding), print self.prev_line = scanline def convert_hor_path_list(self): if DEBUG_POLYGONEXTRACTOR2: print "converting hor paths" hor_path_list = [] for s in self.hor_path_list: allsame = True miny = s.points[0].y maxy = s.points[0].y for p in s.points: if not p.x == s.points[0].x: allsame = False if p.y < miny: miny = p.y if p.y > maxy: maxy = p.y if allsame: if DEBUG_POLYGONEXTRACTOR2: print "all same !" s0 = Path() for p in s.points: if p.y == miny: s0.append(p) hor_path_list.append(s0) s1 = Path() for p in s.points: if p.y == maxy: s1.append(p) hor_path_list.append(s1) continue prev = s.points[-1] p_iter = CyclicIterator(s.points) p = s.points[0] next_p = p_iter.next() while not ((prev.x >= p.x) and (next_p.x > p.x)): p = next_p next_p = p_iter.next() count = 0 while count < len(s.points): s0 = Path() while next_p.x >= p.x: s0.append(p) p = next_p next_p = p_iter.next() count += 1 s0.append(p) while (len(s0.points) > 1) \ and (s0.points[0].x == s0.points[1].x): s0.points = s0.points[1:] while (len(s0.points) > 1) \ and (s0.points[-2].x == s0.points[-1].x): s0.points = s0.points[0:-1] hor_path_list.append(s0) s1 = Path() while next_p.x <= p.x: s1.append(p) p = next_p next_p = p_iter.next() count += 1 s1.append(p) s1.reverse() while (len(s1.points) > 1) \ and (s1.points[0].x == s1.points[1].x): s1.points = s1.points[1:] while (len(s1.points) > 1) \ and (s1.points[-2].x == s1.points[-1].x): s1.points = s1.points[:-1] hor_path_list.append(s1) hor_path_list.sort(cmp=lambda a, b: cmp(a.points[0].x, b.points[0].x)) if DEBUG_POLYGONEXTRACTOR2: print "ver_hor_path_list = ", hor_path_list for s in hor_path_list: print "s%d =" % s.id, for point in s.points: print point.id, print self.ver_hor_path_list = hor_path_list self.act_hor_path_list = [] def process_virtual_hor_scanline(self, next_x): _next_x = next_x while next_x <= _next_x: next_x = INFINITE if self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0].x < next_x): next_x = self.ver_hor_path_list[0].points[0].x if self.act_hor_path_list \ and (self.act_hor_path_list[0].points[0].x < next_x): next_x = self.act_hor_path_list[0].points[0].x if next_x >= _next_x: return if DEBUG_POLYGONEXTRACTOR2: print "ver_hor_path_list =", self.ver_hor_path_list print "act_hor_path_list =", self.act_hor_path_list print "next_x =", next_x if self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0].x <= next_x): while self.ver_hor_path_list \ and (self.ver_hor_path_list[0].points[0].x <= next_x): self.act_hor_path_list.append(self.ver_hor_path_list[0]) self.ver_hor_path_list = self.ver_hor_path_list[1:] self.act_hor_path_list.sort( cmp=lambda a, b: cmp(a.points[0].x, b.points[0].x)) scanline = [] i = 0 while i < len(self.act_hor_path_list): s = self.act_hor_path_list[i] if DEBUG_POLYGONEXTRACTOR2: print "s =", s scanline.append(s.points[0]) if s.points[0].x <= next_x: if len(s.points) <= 1: if DEBUG_POLYGONEXTRACTOR2: print "remove list" self.act_hor_path_list = self.act_hor_path_list[:i] \ + self.act_hor_path_list[i+1:] # TODO: the variable "hor_list_removed" is never used. # Any idea? hor_list_removed = True continue else: if DEBUG_POLYGONEXTRACTOR2: print "remove point", s.points[0] s.points = s.points[1:] if len(s.points) > 0 and s.points[0].x == next_x: # TODO: the variable "repeat" is never used. # Any idea? repeat = True i += 1 self.act_hor_path_list.sort( cmp=lambda a, b: cmp(a.points[0].x, b.points[0].x)) if len(scanline) == 0: return scanline.sort(cmp=lambda a, b: cmp(a.y, b.y)) if DEBUG_POLYGONEXTRACTOR2: print "scanline' =", scanline print "ver_hor_path_list =", self.ver_hor_path_list print "act_hor_path_list =", self.act_hor_path_list self.process_ver_scanline(scanline)