def drawcurve(counter, pos, window): if counter == 1: pygame.draw.circle(window, (0, 255, 0), pos[0], 5) if counter == 2: line = shapely.geometry.LineString([pos[0], pos[1]]) pygame.draw.line(window, (255, 0, 0), pos[0], pos[1]) if counter == 3: pygame.draw.line(window, (255, 0, 0), pos[0], pos[1]) pygame.draw.circle(window, (0, 255, 0), pos[2], 5) if counter == 0: pygame.draw.line(window, (255, 0, 0), pos[0], pos[1]) pygame.draw.line(window, (255, 0, 0), pos[2], pos[3]) rect_lol, kosmos1, kosmos2 = LLuk(pos).get_pygame_arc() pygame.draw.arc(window, (255, 255, 0), rect_lol, kosmos1, kosmos2) lines.append(Line(srumpy(pos[0]), srumpy(pos[1]))) lines.append(Line(srumpy(pos[2]), srumpy(pos[3]))) print(kosmos2 - kosmos1) if (is_right_to(kosmos1, kosmos2)): lines.append( Arc(srumpy(pos[1]), srumpy((intergreat_len, intergreat_len)), 0, False, False, srumpy(pos[2]))) else: lines.append( Arc(srumpy(pos[1]), srumpy((intergreat_len, intergreat_len)), 0, False, True, srumpy(pos[2]))) srodes.append(srumpy(pos[0])) stroke_widths.append(2) srodes.append(srumpy(pos[3])) stroke_widths.append(2) return "kcc" return ""
def fillSvg(svg_path, svg_d, width=750, height=750): p = parse_path(svg_d) intersections = [] for i in range(height, 0, -5): newline = Line(complex(0, i), complex(100000, i)) intersect = p.intersect(newline) # print(intersect) indiv_sections = [] if (intersect): for (T1, seg1, t1), (T2, seg2, t2) in intersect: point = p.point(T1) if point: point_tuple = (point.real, point.imag) # print(point_tuple) indiv_sections.append(point_tuple) # p.append(newline) print(indiv_sections) pairs = list( zip(indiv_sections, indiv_sections[1:] + indiv_sections[:1])) del pairs[1::2] for pair in pairs: x0 = pair[0][0] x1 = pair[1][0] y = pair[0][1] # print("( "+ x0 + ", " + y + "), (" + x1 + ", " + y + ")") betweenLine = Line(complex(x0, y), complex(x1, y)) p.append(betweenLine) disvg(p)
def initialize(self): current_grid = defaultdict(dict) # simplify paths to lines poly_paths = [] for path in self.paths: if path.length() > MINIMUM_STITCH_LENGTH: num_segments = ceil(path.length() / MINIMUM_STITCH_LENGTH) for seg_i in range(int(num_segments)): poly_paths.append(Line(start=path.point(seg_i/num_segments), end=path.point((seg_i+1)/num_segments))) else: poly_paths.append(Line(start=path.start, end=path.end)) bbox = overall_bbox(self.paths) curr_x = int(bbox[0]/MINIMUM_STITCH_LENGTH)*MINIMUM_STITCH_LENGTH total_tests = int(bbox[1]-bbox[0])*int(bbox[3]-bbox[2])/(MINIMUM_STITCH_LENGTH*MINIMUM_STITCH_LENGTH) while curr_x < bbox[1]: curr_y = int(bbox[2]/MINIMUM_STITCH_LENGTH)*MINIMUM_STITCH_LENGTH while curr_y < bbox[3]: test_line = Line(start=curr_x + curr_y * 1j, end=curr_x + MINIMUM_STITCH_LENGTH + ( curr_y + MINIMUM_STITCH_LENGTH) * 1j) start = time() is_contained = path1_is_contained_in_path2(test_line, Path(*poly_paths)) end = time() if is_contained: current_grid[curr_x][curr_y] = False curr_y += MINIMUM_STITCH_LENGTH curr_x += MINIMUM_STITCH_LENGTH self.current_grid = current_grid
def drawcurve(counter, pos, window): global cols if counter == 1: pygame.draw.circle(window, (0, 255, 0), pos[0], 5) if counter == 2: pygame.draw.line(window, (255, 0, 0), pos[0], pos[1]) if counter == 3: pygame.draw.line(window, (255, 0, 0), pos[0], pos[1]) pygame.draw.circle(window, (0, 255, 0), pos[2], 5) if counter == 0: pygame.draw.line(window, (255, 0, 0), pos[0], pos[1]) pygame.draw.line(window, (255, 0, 0), pos[2], pos[3]) rect_lol, kosmos1, kosmos2 = LLuk(pos).get_pygame_arc() pygame.draw.arc(window, (255, 255, 0), rect_lol, kosmos1, kosmos2) srodes.append(srumpy(pos[0])) stroke_widths.append(2) srodes.append(srumpy(pos[3])) stroke_widths.append(2) lines.append(Line(srumpy(pos[0]), srumpy(pos[1]))) lines.append(Line(srumpy(pos[2]), srumpy(pos[3]))) luck = LLuk(pos) lines.append( Line(srumpy(luck.additional_line[0]), srumpy(luck.additional_line[1]))) lines.append(luck.get_svg_arc()) cols = cols + "kkkk"
def make_continuous(path): # takes a discontinuous path (like a donut or a figure 8, and slices it together # such that it is continuous cont_paths = path.continuous_subpaths() paths = cont_paths[0][:] for i in range(1, len(cont_paths)): start_point = paths[-1].end previous_point = paths[0].end # find the index of the closest point on the inner circle inner_start_index = sorted([(j, abs(path.start - start_point)) for j, path in enumerate(cont_paths)], key=lambda x: x[1])[0][0] next_start_index = sorted([(j, abs(path.start - previous_point)) for j, path in enumerate(cont_paths) if j != inner_start_index], key=lambda x: x[1])[0][0] paths += [Line(start=start_point, end=cont_paths[i][inner_start_index].start)] if next_start_index > inner_start_index: paths += cont_paths[i][inner_start_index:] + cont_paths[0:inner_start_index] else: paths += cont_paths[i][len( cont_paths[i]) - inner_start_index:inner_start_index:-1] + cont_paths[ inner_start_index::-1] paths += [Line(start=cont_paths[-1][inner_start_index].start, end=start_point)] return paths
def shorter_side(paths): vector_points = [path.point(0.5) for i, path in enumerate(paths) if i < 4] lines = [ Line(start=vector_points[0], end=vector_points[2]), Line(start=vector_points[1], end=vector_points[3]) ] return 1 if lines[0].length() > lines[1].length() else 0
def create_square_shape(cls, w, h): return [ Line(complex(0, 0), complex(0, h)), Line(complex(0, h), complex(w, h)), Line(complex(w, h), complex(w, 0)), Line(complex(w, 0), complex(0, 0)), ]
def test_generate_straight_stroke(): dig = Digitizer() paths = [Path(*[Line(start=0, end=100), Line(start=100, end=100 + 100j), Line(start=100 + 100j, end=100j), Line(start=100j, end=0)])] dig.stroke_color = (0, 0, 0) dig.scale = 1.0 dig.generate_straight_stroke(paths) assert len(dig.stitches) > len(paths)
def test_fill_scan(): dig = Digitizer() dig.fill_color = (0, 0, 0) paths = Path(*[Line(start=0, end=100), Line(start=100, end=100+100j), Line(start=100+100j, end=100j), Line(start=100j, end=0)]) dig.scale = 1.0 dig.fill = True dig.fill_scan(paths) assert len(dig.stitches) > 0
def test_scan_lines(): paths = Path(*[ Line(start=0, end=100), Line(start=100, end=100 + 100j), Line(start=100 + 100j, end=100j), Line(start=100j, end=0) ]) lines = scan_lines(paths) assert len(lines) > 0
def remove_close_paths(input_paths): if len(input_paths) == 1: if input_paths[0].length() < MINIMUM_STITCH_LENGTH: return [] else: return input_paths def snap_angle(p): hyp = p.length() y_diff = (p.start - p.end).imag if hyp == 0.0: return pi * sign(y_diff) / 2.0 elif y_diff / hyp > 1.0: return pi / 2.0 elif y_diff / hyp < -1.0: return pi / 2.0 else: return asin(y_diff / hyp) paths = [ path for path in input_paths if path.length() >= MINIMUM_STITCH_LENGTH ] # remove any paths that are less than the minimum stitch while len([True for line in paths if line.length() < MINIMUM_STITCH_LENGTH]) > 0 \ or len([paths[i] for i in range(1, len(paths)) if paths[i].start != paths[i - 1].end]) > 0: paths = [ path for path in paths if path.length() >= MINIMUM_STITCH_LENGTH ] paths = [ Line(start=paths[i].start, end=paths[(i + 1) % len(paths)].start) for i in range(0, len(paths)) ] angles = [snap_angle(p) for p in paths] straight_lines = [] current_angle = None current_start = None j = 0 while j < len(angles): if current_angle is None: current_angle = angles[0] current_start = paths[j].start while abs(current_angle - angles[j % len(paths)]) < 0.01: j += 1 straight_lines.append( Line(start=current_start, end=paths[j % len(paths)].start)) current_angle = angles[j % len(angles)] current_start = paths[j % len(paths)].start paths = straight_lines assert len([ i for i in range(1, len(paths)) if paths[i].start != paths[i - 1].end ]) == 0 assert len( [True for line in paths if line.length() < MINIMUM_STITCH_LENGTH]) == 0 return paths
def __init__(self, **kwargs): """Constructor """ byA_FrozenClass.__init__(self) self._from = kwargs.get('P1') self._to = kwargs.get('P2') assert isinstance(self._from, byA_Point) assert isinstance(self._to, byA_Point) self._svgpathtools = Line(self._from.toRI(), self._to.toRI()) self._freeze("byA_Line")
def test_generate_pattern(fill): dig = Digitizer() dig.all_paths = [Path(*[Line(start=0, end=100), Line(start=100, end=100+100j), Line(start=100+100j, end=100j), Line(start=100j, end=0)])] dig.attributes = [{"fill": "black"}] dig.scale = 1.0 dig.fill = fill dig.generate_pattern() assert len(dig.pattern.blocks) > 0 assert len(dig.pattern.blocks[0].stitches) > 0
def trace_image(filecontents): output = StringIO() output.write(filecontents) _image = Image.open(output) pixels = posturize(_image) output_paths = [] attributes = [] for color in pixels: data = zeros(_image.size, uint32) for pixel in pixels[color]: data[pixel[0], pixel[1]] = 1 # Create a bitmap from the array bmp = potrace.Bitmap(data) # Trace the bitmap to a path path = bmp.trace() # Iterate over path curves for curve in path: svg_paths = [] start_point = curve.start_point true_start = curve.start_point for segment in curve: if true_start is None: true_start = segment.start_point if start_point is None: start_point = segment.start_point if isinstance(segment, BezierSegment): svg_paths.append( CubicBezier( start=start_point[1] + 1j * start_point[0], control1=segment.c1[1] + segment.c1[0] * 1j, control2=segment.c2[1] + segment.c2[0] * 1j, end=segment.end_point[1] + 1j * segment.end_point[0])) elif isinstance(segment, CornerSegment): svg_paths.append( Line(start=start_point[1] + 1j * start_point[0], end=segment.c[1] + segment.c[0] * 1j)) svg_paths.append( Line(start=segment.c[1] + segment.c[0] * 1j, end=segment.end_point[1] + 1j * segment.end_point[0])) else: print("not sure what to do with: ", segment) start_point = segment.end_point # is the path closed? if true_start == start_point: output_paths.append(Path(*svg_paths)) color = pixel[2] rgb = "#%02x%02x%02x" % (color[0], color[1], color[2]) fill = rgb attributes.append({"fill": fill, "stroke": rgb}) true_start = None start_point = None svg_paths = [] return output_paths, attributes
def allcond(_t): from andysSVGpathTools import segUnitTangent tt = max(0, min(1, _t.real)) lin2pt = Line(seg.point(tt), pt) real_cond = isclose(_t.imag, 0) bezier_cond = (0 < _t.real < 1 or isclose(_t.real, 0) or isclose(_t.real, 1)) nondeg_cond = (not isclose(seg.derivative(tt), 0) or isclose(dot_prod(segUnitTangent(seg, tt), lin2pt.unit_tangent()), 0)) outward_cond = dot_prod(lin2pt.unit_tangent(), -1j*segUnitTangent(seg, tt)) > 0 return real_cond and bezier_cond and nondeg_cond and outward_cond
def test_stack_paths2(): blo = Path(*[ Line(start=0, end=100), Line(start=100, end=100 + 100j), Line(start=100 + 100j, end=100j), Line(start=100j, end=0) ]) all_paths = [blo, blo.translated(110)] attributes = [{"fill": "black"}, {"fill": "black"}] all_paths_new, attributes_new = stack_paths(all_paths, attributes) assert all_paths == all_paths_new assert attributes_new == attributes
def test_stack_paths(): all_paths = [ Path(*[ Line(start=0, end=100), Line(start=100, end=100 + 100j), Line(start=100 + 100j, end=100j), Line(start=100j, end=0) ]) ] attributes = [{"fill": "black"}] all_paths_new, attributes_new = stack_paths(all_paths, attributes) assert len(all_paths) == len(all_paths_new) assert len(attributes_new) == len(attributes)
def test_jump_reduction(): paths = [] rect_width = 100 rect_height = rect_width / 2 for i in range(3): y_offset = rect_width*i*1j corners = [rect_height, rect_width+rect_height, rect_width+rect_height + rect_height*1j, rect_height*1j+ rect_height] corners = [c+y_offset for c in corners] lines = [Line(start=corners[j], end=corners[(j+1) % len(corners)]) for j in range(len(corners))] _path = Path(*lines) _path = _path.rotated(i*20) paths += list(_path) max_y = max([p.start.imag for p in paths]+[p.end.imag for p in paths]) max_x = max([p.start.real for p in paths]+[p.end.real for p in paths]) filename = "test_jump_reduction.svg" viewbox = [0, -rect_height, max_x+2*rect_height, max_y+2*rect_height] dwg = Drawing(filename, width="10cm", viewBox=" ".join([str(b) for b in viewbox])) dwg.add(dwg.path(d=Path(*paths).d())) dwg.save() dig = Digitizer() dig.filecontents = open(filename, "r").read() dig.svg_to_pattern() pattern_to_svg(dig.pattern, join(filename + ".svg"))
def closePath(self): if self._contour[0].start != self._contour[-1].end: self._contour.append( Line(self._contour[-1].end, self._contour[0].start)) self._outline.append(self._contour) self._contour = SVGPathContour() self.logger.debug("closePath()")
def snap(self, tree, threshold): def process(points): for i, p in enumerate(points): best, _, dist = tree.nearest_neighbor([p.real, p.imag]) if dist < threshold: points[i] = complex(best[0], best[1]) return points path = parse_path(self['d']) newPath = Path() for seg in path: points = process([seg.start, seg.end]) if isinstance(seg, Line): newSeg = Line(*points) newPath.append(newSeg) elif isinstance(seg, CubicBezier): newSeg = CubicBezier(points[0], seg.control1, seg.control2, points[1]) newPath.append(newSeg) self['d'] = newPath.d() return self
def flip_path(upside_down_path): path = [] _, _, min_y, max_y = upside_down_path.bbox() offset = max_y + min_y for segment in upside_down_path._segments: if type(segment) is Line: path.append( Line(complex(segment.start.real, -segment.start.imag + offset), complex(segment.end.real, -segment.end.imag + offset))) elif type(segment) is Arc: path.append( Arc(complex(segment.start.real, -segment.start.imag + offset), segment.radius, abs(180 - segment.rotation), segment.large_arc, not segment.sweep, complex(segment.end.real, -segment.end.imag + offset))) elif type(segment) is QuadraticBezier: path.append( QuadraticBezier( complex(segment.start.real, -segment.start.imag + offset), complex(segment.control.real, -segment.control.imag + offset), complex(segment.end.real, -segment.end.imag + offset))) else: raise ValueError(f"Unknown type: {type(segment)}") return Path(*path)
def pattern_to_svg(pattern, filename): if isinstance(filename, str) or isinstance(filename, unicode): output_file = open(filename, "wb") else: output_file = filename paths = [] colors = [] scale_factor = 0.1 # scale from cm to mm from pes for block in pattern.blocks: block_paths = [] last_stitch = None for stitch in block.stitches: if "JUMP" in stitch.tags: last_stitch = stitch continue if last_stitch is None: last_stitch = stitch continue block_paths.append( Line(start=last_stitch.complex * scale_factor, end=stitch.complex * scale_factor)) last_stitch = stitch if len(block_paths) > 0: colors.append(block.tuple_color) paths.append(Path(*block_paths)) dims = overall_bbox(paths) mindim = max(dims[1] - dims[0], dims[3] - dims[2]) print("in pattern to svg, overallbbox", overall_bbox(paths)) if len(paths) == 0: print("warning: pattern did not generate stitches") return wsvg(paths, colors, filename=output_file, mindim=mindim)
def path_union(path1, path2): # trace around the outside of two paths to make a union # todo: this is pretty tricky to implement and at the moment, this is incomplete output_segments = [] paths = [path1, path2] if path1_is_contained_in_path2(path1, path2): return path2 elif path1_is_contained_in_path2(path2, path1): return path1 indexes = [0, 0] current_path = 0 # first, check whether the first segment is within the second path so that we can # find the start location. If it is, keep going until you find the first segment that # isn't within the other path while path1_is_contained_in_path2(paths[current_path][indexes[current_path]], paths[not current_path]) and indexes[current_path] < len(paths[current_path]): indexes[current_path] += 1 # does the current path intersect the other path? intersections = paths[not current_path].intersect(paths[current_path][indexes[current_path]]); if len(intersections) > 0: # we need to find out whether the start point is within shape 2 test_line = Line(start=paths[current_path][indexes[current_path]].start, end=paths[current_path][indexes[current_path]].end) if path1_is_contained_in_path2(test_line, paths[not current_path]): start_point = None start_point = None else: start_point = paths[current_path][indexes[current_path]].start return output_segments
def cross_stitch_to_pattern(self, _image): # this doesn't work well for images with more than 2-3 colors max_dimension = max(_image.size) pixel_ratio = int(max_dimension * MINIMUM_STITCH_LENGTH / (4 * 25.4)) if pixel_ratio != 0: _image = _image.resize( (_image.size[0] / pixel_ratio, _image.size[1] / pixel_ratio)) pixels = posturize(_image) paths = [] attrs = [] for color in pixels: for pixel in pixels[color]: rgb = "#%02x%02x%02x" % (pixel[2][0], pixel[2][1], pixel[2][2]) x = pixel[0] y = pixel[1] attrs.append({"fill": "none", "stroke": rgb}) paths.append( Path( Line(start=x + 1j * y, end=x + 0.5 * MINIMUM_STITCH_LENGTH + 1j * (y + MINIMUM_STITCH_LENGTH)))) debug_paths = [[path, attrs[i]["fill"], attrs[i]["stroke"]] for i, path in enumerate(paths)] write_debug("png", debug_paths) self.all_paths = paths self.attributes = attrs self.scale = 1.0 self.generate_pattern()
def shape_to_path(shape): new_path = [] if shape.area > 0.0: points = shape.exterior.coords # close the path new_path.append(Line(start=points[-1][0] + points[-1][1] * 1j, end=points[0][0] + points[0][1] * 1j)) elif shape.length > 0.0: points = shape.coords else: return [] for i in range(len(points) - 1): new_path.append(Line(start=points[i - 1][0] + points[i - 1][1] * 1j, end=points[i][0] + points[i][1] * 1j)) return Path(*new_path)
def transform_side(sides, targets, angle_offset=0): def angle(point1, point2): diff = point1 - point2 if diff.real == 0: return 90.0 return atan(diff.imag / diff.real) * 180.0 / pi # change this so that it has two targets transformed_side = Path(*sides) source_angle = angle(transformed_side.end, transformed_side.start) - \ angle(targets[0], targets[1]) transformed_side = transformed_side.rotated(-source_angle + angle_offset) source = transformed_side.end if angle_offset == 0 else transformed_side.start diff = targets[1] - source transformed_side = transformed_side.translated(diff) draw_marker(targets[0], rgb(0, 200, 200)) draw_marker(targets[1], rgb(0, 255, 255)) transformed_diff = abs(transformed_side.start - transformed_side.end) targets_diff = abs(targets[0] - targets[1]) if transformed_diff < targets_diff: transformed_side.insert( 0, Line(start=targets[0], end=transformed_side.start)) elif transformed_diff > targets_diff: # pop elements off until the transformed diff is smaller while transformed_diff > targets_diff: transformed_side.pop(0) transformed_diff = abs(transformed_side.start - transformed_side.end) print("path", transformed_side) print("path is longer", transformed_diff - targets_diff) return transformed_side
class byA_Line(byA_FrozenClass): def __init__(self, **kwargs): """Constructor """ byA_FrozenClass.__init__(self) self._from = kwargs.get('P1') self._to = kwargs.get('P2') assert isinstance(self._from, byA_Point) assert isinstance(self._to, byA_Point) self._svgpathtools = Line(self._from.toRI(), self._to.toRI()) self._freeze("byA_Line") def toRI(self): """Point as a complex """ return complex(self._from.toRI()), complex(self._to.toRI()) def toSVGWrite(self, drawing, **extra): """to the svgwrite syntax """ return drawing.path(d=self.toStr(), **extra) def toStr(self): return Path(self._svgpathtools).d() def reverse(self): return byA_Line(P1=self._to, P2=self._from) def rotate(self, degre, origin=None): self._svgpathtools = self._svgpathtools.rotated(degre, origin) self._from.rotate(degre, origin) self._to.rotate(degre, origin) def rotated(self, degre, origin=None): res = byA_Line(P1=self._from, P2=self._to) res.rotate(degre, origin) return res def coeffDir(self): assert (self._to._x != self._from._x) return (self._to._y - self._from._y) / (self._to._x - self._from._x) def equationLine(self): assert (self._to._x != self._from._x) # y = ax+b # src._y = a*src._x + b # dst._y = a*dst._x + b a = self.coeffDir() return [a, self._from._y - a * self._from._x] def split(self, t): p = self._from + t * (self._to - self._from) return (byA_Line(P1=self._from, P2=p), byA_Line(P1=p, P2=self._to)) def lenght(self): """to the svgwrite syntax """ return sqrt((self._from.toRI().real - self._to.toRI().real)**2 + (self._from.toRI().imag - self._to.toRI().imag)**2)
def __init__(self, **kwargs): byA_FrozenClass.__init__(self) self._from = kwargs.get('P1') self._to = kwargs.get('P2') assert isinstance(self._from, byA_Point) assert isinstance(self._to, byA_Point) self._svgline = Line(self._from.toRI(), self._to.toRI()) self._freeze("byA_Line")
def connect_pins(self, c1, p1, c2, p2, **kwargs): start_pin = self.board_component_pin_location(c1, p1) end_pin = self.board_component_pin_location(c2, p2) self.add_route([ Line(complex(start_pin[0], start_pin[1]), complex(end_pin[0], end_pin[1])) ], **kwargs)
def generateInBetweens(poseA, poseB, steps): inv = Inventory() # make pairs pairs = [] for key in ORDER: if key in poseA.inv and key in poseB.inv: partA = poseA.inv[key] partB = poseB.inv[key] if len(partA) != 1 or len(partB) != 1: print('Too many parts {0} - A: {1} B: {2}'.format( key, partA.keys(), partB.keys())) continue pairs.append((key, partA.values()[0], partB.values()[0])) # If there are 3 steps, there are 4 gaps between start and finish # |------1------2------3------| gaps = steps + 1 # process pairs for key, a, b in pairs: pathA = parse_path(a['d']) pathB = parse_path(b['d']) if len(pathA) != len(pathB): print('Unmatched segments {0} - A: {1} B: {2}'.format( key, pathA, pathB)) continue for step in range(1, gaps): newPath = Path() for i in range(len(pathA)): segA = pathA[i] segB = pathB[i] if isinstance(segA, Line): points = _deltaPoints([segA.start, segA.end], [segB.start, segB.end], step, gaps) newPath.append(Line(*points)) elif isinstance(segA, CubicBezier): points = _deltaPoints( [segA.start, segA.control1, segA.control2, segA.end], [segB.start, segB.control1, segB.control2, segB.end], step, gaps) newPath.append(CubicBezier(*points)) newPart = Part(newPath.d()) newPart['x'] = int(_delta(a['x'], b['x'], step, gaps)) newPart['y'] = int(_delta(a['y'], b['y'], step, gaps)) newPart['z'] = int(_delta(a['z'], b['z'], step, gaps)) inv.addPart(key, newPart) print(key, step, newPart) return inv
def displaySVGPaths_transects(ring_list, data_transects, transect_angles, skipped_angle_indices, fn=None): if not fn: filename = opt.output_directory + ring_list[0].svgname else: filename = fn transectPaths = [] for tran_index in range(len(data_transects)): tran_path = Path() for seg_index in range(len(data_transects[tran_index]) - 1): start_pt = data_transects[tran_index][seg_index] end_pt = data_transects[tran_index][seg_index + 1] tran_path.append(Line(start_pt,end_pt)) transectPaths.append(tran_path) ringPaths = [r.path for r in ring_list] ringColors = [r.color for r in ring_list] pathList = ringPaths + transectPaths colors = ringColors + ['black']*len(transectPaths) transect_nodes = [item for sublist in data_transects for item in sublist] # flatten data_transects nodes = transect_nodes + [ring_list[0].center] node_colors = ['purple']*len(transect_nodes) + ['blue'] text = ['%.3f' % theta for idx, theta in enumerate(transect_angles) if idx not in skipped_angle_indices] text += ['skipped %.3f' % transect_angles[idx] for idx in skipped_angle_indices] text_path = [] for tr in data_transects: end = tr[-1] last_seg = Line(tr[-2], tr[-1]) u = last_seg.unit_tangent(1) text_path.append(Path(Line(end + 10*u, end + 100*u))) # handle skipped transects bdry_ring = max(ring_list, key=lambda ring: ring.maxR) bdry_length = bdry_ring.path.length() for idx in skipped_angle_indices: s = bdry_length * transect_angles[idx] T = inv_arclength(bdry_ring.path, s) u = bdry_ring.path.normal(T) end = bdry_ring.path.point(T) text_path.append(Line(end + 10*u, end + 100*u)) wsvg(pathList, colors, nodes=nodes, node_colors=node_colors, text=text, text_path=text_path, filename=filename+'_transects.svg')
def offset_curve(path, offset_distance, steps=1000): """Takes in a Path object, `path`, and a distance, `offset_distance`, and outputs an piecewise-linear approximation of the 'parallel' offset curve.""" nls = [] for seg in path: for k in range(steps): t = k / float(steps) offset_vector = offset_distance * seg.normal(t) nl = Line(seg.point(t), seg.point(t) + offset_vector) nls.append(nl) connect_the_dots = [ Line(nls[k].end, nls[k + 1].end) for k in range(len(nls) - 1) ] if path.isclosed(): connect_the_dots.append(Line(nls[-1].end, nls[0].end)) offset_path = Path(*connect_the_dots) return offset_path
def generate_unsorted_transects(ring_list, center): from options4rings import basic_output_on, warnings_output_on, N_transects, unsorted_transect_debug_output_folder, unsorted_transect_debug_on, colordict from misc4rings import transect_from_angle, normalLineAt_t_toInnerSeg_intersects_withOuter from andysSVGpathTools import pathlistXlineIntersections from andysmod import Timer import operator from random import uniform #Find outer boundary ring for r in ring_list: if r.color == colordict['boundary']: boundary_ring = r break else: warnings_output_on.dprint("[Warning:] Having trouble finding outer boundary - it should be color %s. Will now search for a ring of a similar color and if one is found, will use that.\n"%colordict['boundary']) from misc4rings import closestColor for r in ring_list: if colordict['boundary'] == closestColor(r.color,colordict): boundary_ring = r basic_output_on.dprint("Found a ring of color %s, using that one."%r.color) break else: warnings_output_on.dprint("[Warning:] Outer boundary could not be found by color (or similar color). This is possibly caused by the outer boundary ring not being closed - in this case you'd be able to see a (possibly quite small) gap between it's startpoint and endpoint. Using the ring of greatest maximum radius as the boundary ring (and hoping if there is a gap none of the transects hit it).\n") keyfcn = lambda x: x.maxR boundary_ring = max(ring_list,key=keyfcn) #Find transects from time import time as current_time from andysmod import format_time tr_gen_start_time = current_time() data = [] data_indices = [] angles = [] for dummy_index in range(N_transects): #dummy_index only used to create loop #estimate time remaining if dummy_index != 0: total_elapsed_time = current_time() - tr_gen_start_time estimated_time_remaining = (N_transects - dummy_index)*total_elapsed_time/dummy_index timer_str = 'Transect %s of %s || Est. Remaining Time = %s || Elapsed Time = %s'%(dummy_index+1,N_transects,format_time(estimated_time_remaining),format_time(total_elapsed_time)) overwrite_progress = True else: timer_str = 'transect %s of %s'%(dummy_index+1,N_transects) overwrite_progress = False print('') #generate current transect with Timer(timer_str, overwrite=overwrite_progress): if unsorted_transect_debug_on: print('') test_angle = uniform(0, 1) # test_angle = 0.408 angles.append(test_angle) transect = [center] transect_rings = ['core'] unused_ring_indices = range(len(ring_list)) #used to keep track of which rings I've used and thus don't need to be checked in the future # Find first transect segment (from core/center) # normal line to use to find intersections (from center to boundary ring) nl2bdry, seg_outer, t_outer = transect_from_angle(test_angle, center, boundary_ring.path, 'debug') #make normal line a little longer nl2bdry = Line(nl2bdry.start, nl2bdry.start + 1.5*(nl2bdry.end-nl2bdry.start)) tmp = pathlistXlineIntersections(nl2bdry, [ring_list[i].path for i in unused_ring_indices]) (tl,path_index,seg,tp) = min(tmp, key=operator.itemgetter(0)) #(tl,path_index,seg,tp) transect.append(nl2bdry.point(tl)) transect_rings.append(unused_ring_indices[path_index]) del unused_ring_indices[path_index] #now for the rest of the transect num_rings_checked = 0 while (ring_list[transect_rings[-1]] != boundary_ring and num_rings_checked < len(ring_list)): # < is correct, already did first num_rings_checked += 1 inner_path = ring_list[transect_rings[-1]].path inner_t = tp inner_seg = seg # normal line to use to find intersections (from center to boundary ring) nl2bdry, seg_outer, t_outer = normalLineAt_t_toInnerSeg_intersects_withOuter(inner_t, inner_seg, boundary_ring.path, center, 'debug') # make normal line a little longer nl2bdry = Line(nl2bdry.start,nl2bdry.start + 1.5*(nl2bdry.end-nl2bdry.start)) normal_line_intersections = pathlistXlineIntersections(nl2bdry, [ring_list[i].path for i in unused_ring_indices]) try: # (tl,path_index,seg,tp) tl, path_index, seg, tp = min(normal_line_intersections, key=operator.itemgetter(0)) except ValueError: raise if unsorted_transect_debug_on: from andysmod import format001 inner_path_index = transect_rings[-1] used_ring_paths = [r.path for i,r in enumerate(ring_list) if i not in unused_ring_indices+[inner_path_index]] used_ring_colors = ['black']*len(used_ring_paths) unused_ring_paths = [ring_list[i].path for i in unused_ring_indices] unused_ring_colors = [ring_list[i].color for i in unused_ring_indices] transect_so_far = Path(*[Line(transect[i-1],transect[i]) for i in range(1,len(transect))]) paths = used_ring_paths + unused_ring_paths + [transect_so_far] +[inner_path] + [nl2bdry] colors = used_ring_colors + unused_ring_colors + ['green']+['blue'] + ['black'] nodes_so_far = transect[1:-1] potential_nodes = [nl2bdry.point(tltmp) for (tltmp,path_indextmp,segtmp,tptmp) in normal_line_intersections] nodes = nodes_so_far + potential_nodes node_colors = ['red']*len(nodes_so_far) + ['purple']*len(potential_nodes) save_name = unsorted_transect_debug_output_folder+'unsorted_transect_debug_%s.svg'%format001(3,len(transect)) disvg(paths,colors,nodes=nodes,node_colors=node_colors,center=center,filename=save_name,openInBrowser=False) print("Done with %s out of (at most) %s transect segments"%(len(transect),len(ring_list))) transect.append(nl2bdry.point(tl)) transect_rings.append(unused_ring_indices[path_index]) del unused_ring_indices[path_index] data.append(transect) data_indices.append(transect_rings) return data, data_indices, angles
def generate_sorted_transects(ring_list, center, angles2use=None): from options4rings import basic_output_on, N_transects from misc4rings import transect_from_angle, normalLineAt_t_toInnerSeg_intersects_withOuter from andysSVGpathTools import pathlistXlineIntersections from andysmod import Timer, format_time from svgpathtools import Line from random import uniform from time import time as current_time from operator import itemgetter tmp = sorted(enumerate(ring_list), key = lambda tup: tup[1].sort_index) ring_sorting, sorted_ring_list = zip(*tmp) unsorted_index = lambda idx: ring_sorting[idx] #Find transects tr_gen_start_time = current_time() data = [] data_indices = [] angles = [] for dummy_index in range(N_transects): #estimate time remaining if dummy_index != 0: total_elapsed_time = current_time() - tr_gen_start_time estimated_time_remaining = (N_transects - dummy_index)*total_elapsed_time/dummy_index timer_str = 'Transect %s of %s || Est. Remaining Time = %s || Elapsed Time = %s'%(dummy_index+1,N_transects,format_time(estimated_time_remaining),format_time(total_elapsed_time)) overwrite_progress = True else: timer_str = 'transect %s of %s'%(dummy_index+1,N_transects) overwrite_progress = False print('') #generate current transect with Timer(timer_str,overwrite=overwrite_progress): # sorted_closed_rings = (r for r in sorted_ring_list if r.isClosed()) if angles2use: test_angle = angles2use[dummy_index] else: test_angle = uniform(0,1) # test_angle = 0.408 angles.append(test_angle) transect = [center] transect_rings = ['core'] # find first (innermost) closed ring # next_closed_ring = sorted_closed_rings.next() next_closed_ring = next(r for r in sorted_ring_list if r.isClosed()) next_closed_ring_sidx = next_closed_ring.sort_index #Find first transect segment (from core/center) # Start by finding line that leaves center at angle and goes to # the first closed ring nl2bdry, seg_outer, t_outer = transect_from_angle(test_angle, center, next_closed_ring.path, 'debug') # Make normal line a little longer end2use = nl2bdry.start + 1.5*(nl2bdry.end - nl2bdry.start) nl2bdry = Line(nl2bdry.start, end2use) pot_paths = [r.path for r in sorted_ring_list[0:next_closed_ring_sidx + 1]] #Note: intersections returned as (tl, path_index, seg, tp) pot_path_inters = pathlistXlineIntersections(nl2bdry, pot_paths) tl, path_index, seg, tp = min(pot_path_inters, key=itemgetter(0)) #updates transect.append(nl2bdry.point(tl)) transect_rings.append(unsorted_index(path_index)) cur_pos_si = path_index next_closed_ring = next(r for r in sorted_ring_list if (r.sort_index > cur_pos_si and r.isClosed())) # next_closed_ring = sorted_closed_rings.next() next_closed_ring_sidx = next_closed_ring.sort_index #now for the rest of the transects num_rings_checked = 0 while (cur_pos_si < len(ring_list) - 1 and num_rings_checked < len(ring_list)): # < is correct, already did first num_rings_checked += 1 inner_t = tp inner_seg = seg # Find outwards normal line from current position to the next # closed ring nl2bdry, seg_outer, t_outer = normalLineAt_t_toInnerSeg_intersects_withOuter(inner_t, inner_seg, next_closed_ring.path, center, 'debug') # Make the normal line a bit longer to avoid numerical error end2use = nl2bdry.start + 1.5*(nl2bdry.end - nl2bdry.start) nl2bdry = Line(nl2bdry.start, end2use) pot_paths = [r.path for r in sorted_ring_list[cur_pos_si+1:next_closed_ring_sidx+1]] tl, path_index, seg, tp = min(pathlistXlineIntersections(nl2bdry,pot_paths), key=itemgetter(0)) #updates transect.append(nl2bdry.point(tl)) cur_pos_si += path_index + 1 transect_rings.append(unsorted_index(cur_pos_si)) if cur_pos_si < len(ring_list)-1: # next_closed_ring = sorted_closed_rings.next() next_closed_ring = next(r for r in sorted_ring_list if (r.sort_index > cur_pos_si and r.isClosed())) next_closed_ring_sidx = next_closed_ring.sort_index data.append(transect) data_indices.append(transect_rings) return data, data_indices, angles