Beispiel #1
0
def path_to_polygon(path):
    polygon = []
    global max_x, max_y, min_x, min_y
    new_path = Path()
    for segment in parse_path(path):
        new_path.append(Line(segment.start, segment.end))
    new_path.closed = True
    raw_path = new_path.d()

    # search for compound path bits and remove them
    path_bits = re.findall('M.+?[ZM]', raw_path)
    if len(path_bits) > 0:
        raw_path = path_bits[0]
        raw_path_size = len(re.findall(',', raw_path))
        for bit in path_bits:
            bit_size = len(re.findall(',', bit))
            if bit_size > raw_path_size:
                raw_path = bit
                raw_path_size = bit_size

    # convert to simple list of nodes
    nodes = re.findall('[ML]\s*(\d+\.*\d*,\d+\.*\d*)\s*', raw_path)
    for n in nodes:
        coords = n.split(',')
        if max_x < int(coords[0]):
            max_x = int(coords[0])
        if max_y < int(coords[1]):
            max_y = int(coords[1])
        if min_x > int(coords[0]):
            min_x = int(coords[0])
        if min_y > int(coords[1]):
            min_y = int(coords[1])
        polygon.append([int(coords[0]), int(coords[1])])
    polygon.pop()
    return polygon
Beispiel #2
0
def createSVG(filename, tool, points):
  # Generate the paths
  allPoints = list()
  paths = list()
  t = Template(PATH_TEMPLATE)
  for point in points:
    allPoints.extend(point)
    path = Path()
    lx, ly = point[0]
    pc = 0
    for p in point[1:]:
      l = mkLine(lx, ly, p[0], p[1])
      if l is not None:
        path.append(l)
        pc = pc + 1
      lx = p[0]
      ly = p[1]
    path.closed = True
    paths.append(t.safe_substitute({ 'strokeWidth': tool, 'path': path.d() }))
    print "Generated path with %d points." % pc
  # Build the final output
  v = { 
    'xmin': min([ x for x, y in allPoints ]) - (tool / 2),
    'ymin': min([ y for x, y in allPoints ]) - (tool / 2),
    'xmax': max([ x for x, y in allPoints ]) + (tool / 2),
    'ymax': max([ y for x, y in allPoints ]) + (tool / 2)
    }
  v['width'] = abs(v['xmax'] - v['xmin'])
  v['height'] = abs(v['ymax'] - v['ymin'])
  v['path'] = "\n".join(paths)
  out = Template(SVG_TEMPLATE).substitute(v).strip()
  # And write the file
  with open(filename, "w") as svg:
    svg.write(out)
Beispiel #3
0
def createSVG(filename, tool, points):
    # Generate the paths
    allPoints = list()
    paths = list()
    t = Template(PATH_TEMPLATE)
    for point in points:
        allPoints.extend(point)
        path = Path()
        lx, ly = point[0]
        pc = 0
        for p in point[1:]:
            l = mkLine(lx, ly, p[0], p[1])
            if l is not None:
                path.append(l)
                pc = pc + 1
            lx = p[0]
            ly = p[1]
        path.closed = True
        paths.append(t.safe_substitute({
            'strokeWidth': tool,
            'path': path.d()
        }))
        print "Generated path with %d points." % pc
    # Build the final output
    v = {
        'xmin': min([x for x, y in allPoints]) - (tool / 2),
        'ymin': min([y for x, y in allPoints]) - (tool / 2),
        'xmax': max([x for x, y in allPoints]) + (tool / 2),
        'ymax': max([y for x, y in allPoints]) + (tool / 2)
    }
    v['width'] = abs(v['xmax'] - v['xmin'])
    v['height'] = abs(v['ymax'] - v['ymin'])
    v['path'] = "\n".join(paths)
    out = Template(SVG_TEMPLATE).substitute(v).strip()
    # And write the file
    with open(filename, "w") as svg:
        svg.write(out)
Beispiel #4
0
    def remove_redundant_lines(self):
        eps = 0.001

        def get_slope_intersect(p1, p2):
            if abs(p1.real - p2.real) < eps:
                # Vertical; no slope and return x-intercept
                return None, p1.real
            m = (p2.imag - p1.imag) / (p2.real - p1.real)
            b1 = p1.imag - m * p1.real
            b2 = p2.imag - m * p2.real
            assert abs(b1 - b2) < eps
            return m, b1

        lines_bucketed_by_slope_intersect = defaultdict(list)
        paths = self.svg_node.getElementsByTagName('path')
        overall_index = 0
        for path_index, path in enumerate(paths):
            path_text = path.attributes['d'].value
            path_obj = parse_path(path_text)
            for line_index, line in enumerate(path_obj):
                slope, intersect = get_slope_intersect(line.start, line.end)
                if slope is not None:
                    slope = round(slope, ndigits=3)
                intersect = round(intersect, ndigits=3)
                lines_bucketed_by_slope_intersect[(slope, intersect)].append({
                    'overall_index': overall_index,
                    'path_index': path_index,
                    'line_index': line_index,
                    'line': line,
                })
                overall_index += 1

        to_remove = {}
        for slope_intersect, lines in lines_bucketed_by_slope_intersect.items():
            # Naive N^2 search for overlapping lines within a slope-intersect bucket
            for i in range(len(lines)):
                line1 = lines[i]['line']
                eq1 = get_slope_intersect(line1.start, line1.end)
                for j in range(i + 1, len(lines)):
                    line2 = lines[j]['line']
                    eq2 = get_slope_intersect(line2.start, line2.end)

                    # Compare slopes
                    if (eq1[0] is None and eq2[0] is None) \
                            or (eq1[0] is not None and eq2[0] is not None and abs(eq1[0] - eq2[0]) < eps):
                        # Compare intersects
                        if abs(eq1[1] - eq2[1]) < eps:
                            # Must be collinear, check for overlap
                            l1x1 = min(line1.start.real, line1.end.real)
                            l1x2 = max(line1.start.real, line1.end.real)
                            l1y1 = min(line1.start.imag, line1.end.imag)
                            l1y2 = max(line1.start.imag, line1.end.imag)

                            l2x1 = min(line2.start.real, line2.end.real)
                            l2x2 = max(line2.start.real, line2.end.real)
                            l2y1 = min(line2.start.imag, line2.end.imag)
                            l2y2 = max(line2.start.imag, line2.end.imag)

                            if l1x1 <= l2x1 + eps and l1x2 + eps >= l2x2 and l1y1 <= l2y1 + eps and l1y2 + eps >= l2y2:
                                # Overlapping, line 1 is bigger
                                assert line1.length() + eps >= line2.length()
                                to_remove[lines[j]['overall_index']] = (lines[j]['path_index'], lines[j]['line_index'], line2)
                            elif l1x1 + eps >= l2x1 and l1x2 <= l2x2 + eps and l1y1 + eps >= l2y1 and l1y2 <= l2y2 + eps:
                                # Overlapping, line 2 is bigger
                                assert line2.length() + eps >= line1.length()
                                to_remove[lines[i]['overall_index']] = (lines[i]['path_index'], lines[i]['line_index'], line1)

        # Reconstruct the paths, but excluding the redundant lines we just identified
        i = 0
        removed = 0
        removed_length = 0
        kept = 0
        kept_length = 0
        for path_index, path in enumerate(paths):
            path_text = path.attributes['d'].value
            path_obj = parse_path(path_text)

            filtered_path = Path()

            for line_index, line in enumerate(path_obj):
                if i in to_remove:
                    assert path_index == to_remove[i][0]
                    assert line_index == to_remove[i][1]
                    assert line == to_remove[i][2]
                    removed += 1
                    removed_length += line.length()
                else:
                    filtered_path.append(line)
                    kept += 1
                    kept_length += line.length()
                i += 1

            # Update the path data with the filtered path data
            path.attributes['d'] = filtered_path.d()

        print 'Removed {} lines ({} length) and kept {} lines ({} length)'.format(
            removed,
            removed_length,
            kept,
            kept_length,
        )

        return [to_remove[k][2] for k in to_remove]
Beispiel #5
0
SVG2 = '''  </g>
</svg>
'''

svgpath1 = '''<path
       style="fill:none;fill-rule:evenodd;stroke:#{0:02x}{1:02x}{2:02x};stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="'''
svgpath2 = ''' Z"/>
'''

print(SVG1)
for p in paths:
	path     = parse_path(p.attrib["d"])
	start    = None
	myrange  = list(frange(0, 1, decimal.Decimal('0.01')))
	mycolors = list(red.range_to(blue, len(myrange)))
	
	for i, px in enumerate(myrange):
		new_path = Path()
		if start is None:
			start = path.point(float(px))
		else:
			end   = path.point(float(px))
			new_path.append(Line(start, end))
			start = end
			color = mycolors[i]
			r = int(color.red * 255)
			g = int(color.green * 255)
			b = int(color.blue * 255)
			print((svgpath1 + new_path.d() + svgpath2).format(clamp(r), clamp(g), clamp(b)))
print(SVG2)
Beispiel #6
0
    def remove_redundant_lines(self):
        lines_bucketed_by_slope_intersect = defaultdict(list)
        paths = self.svg_node.getElementsByTagName('path')
        overall_index = 0
        for path_index, path in enumerate(paths):
            path_text = path.attributes['d'].value
            path_obj = parse_path(path_text)
            for line_index, line in enumerate(path_obj):
                slope, intersect = _get_slope_intersect(line.start, line.end)

                # TODO: float inaccuracy and rounding may cause collinear lines to end up in separate buckets in rare
                # cases, so this is not quite correct. Would be better to put lines into *2* nearest buckets in each
                # dimension to avoid edge cases.
                if slope is not None:
                    slope = round(slope, ndigits=3)
                intersect = round(intersect, ndigits=3)
                lines_bucketed_by_slope_intersect[(slope, intersect)].append({
                    'overall_index': overall_index,
                    'path_index': path_index,
                    'line_index': line_index,
                    'line': line,
                })
                overall_index += 1

        to_remove = {}
        to_update = {}
        for slope_intersect, lines in lines_bucketed_by_slope_intersect.items():
            for i in range(20):
                if SvgProcessor._pairwise_overlap_check(lines, to_update, to_remove):
                    print 'Re-running pairwise overlap check because of updated/merged line'
                    continue
                break
            else:
                raise Exception(
                    'Exceeded the max number of pairwise overlap check passes. Something is probably wrong.'
                )

        # Reconstruct the paths, but excluding/updating the lines we just identified
        i = 0
        removed = 0
        removed_length = 0
        kept = 0
        kept_length = 0
        for path_index, path in enumerate(paths):
            path_text = path.attributes['d'].value
            path_obj = parse_path(path_text)

            filtered_path = Path()

            for line_index, line in enumerate(path_obj):
                if i in to_remove:
                    assert path_index == to_remove[i][0]
                    assert line_index == to_remove[i][1]
                    removed += 1
                    removed_length += line.length()
                elif i in to_update:
                    assert path_index == to_update[i][0]
                    assert line_index == to_update[i][1]
                    replacement_line = to_update[i][2]

                    filtered_path.append(replacement_line)
                    kept += 1
                    kept_length += replacement_line.length()
                else:
                    filtered_path.append(line)
                    kept += 1
                    kept_length += line.length()
                i += 1

            # Update the path data with the filtered path data
            path.attributes['d'] = filtered_path.d()

        print 'Removed {} lines ({} length) and kept {} lines ({} length)'.format(
            removed,
            removed_length,
            kept,
            kept_length,
        )

        return [to_remove[k][2] for k in to_remove], [to_update[k][2] for k in to_update]
Beispiel #7
0
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="'''
svgpath2 = ''' Z"/>
'''

print(SVG1)
for p in paths:
    new_path = Path()
    path = parse_path(p.attrib["d"])
    start = None
    for command in path:
        if type(command) == Line:
            new_path.append(command)
            start = command.end
            ##print(command)
        else:
            not_line_path = Path(command)
            for px in list(frange(0, 1, decimal.Decimal(factor))):
                if start is None:
                    start = not_line_path.point(float(px))
                else:
                    end = not_line_path.point(float(px))
                    new_command = Line(start, end)
                    new_path.append(new_command)
                    ##print(new_command)
                    start = end

    new_path_str = new_path.d()
    start = None
    print(svgpath1 + new_path_str + svgpath2)
print(SVG2)
Beispiel #8
0
SVG1 = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg    xmlns:dc="http://purl.org/dc/elements/1.1/"   xmlns:cc="http://creativecommons.org/ns#"   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"   xmlns:svg="http://www.w3.org/2000/svg"   xmlns="http://www.w3.org/2000/svg"    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="500" height="500">
<g>
'''

SVG2 = '''  </g>
</svg>
'''

svgpath1 = '''<path
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="'''
svgpath2 = ''' Z"/>
'''

print(SVG1)
for p in paths:
    new_path = Path()
    path = parse_path(p.attrib["d"])
    start = None
    #for px in list(frange(0, 1, decimal.Decimal('0.05'))):
    for px in list(frange(0, 1, decimal.Decimal('0.005'))):
        if start is None:
            start = path.point(float(px))
            print(start)
        else:
            end = path.point(float(px))
            new_path.append(Line(start, end))
            start = end
    print(svgpath1 + new_path.d() + svgpath2)
print(SVG2)