コード例 #1
0
ln_0_2_back.translate(trans2)
ln_6_26_back = ln(pt6, pt26, stroke=col_gds)
ln_6_26_back.translate(trans2)
ln_30_29_back = ln(pt30, pt29, stroke=col_gds)
ln_30_29_back.translate(trans2)
dwg.add(ln_0_2_back)
dwg.add(ln_6_26_back)
dwg.add(ln_30_29_back)

# Draw paths

# Path1
seg = Line(complex(pt10[0] * cm, pt10[1] * cm),
           complex(pt6[0] * cm, pt6[1] * cm))
t = seg.ilength(1.0 * cm)
pt10A = pt("10A", np.real(seg.point(t)) / cm, np.imag(seg.point(t)) / cm)

p = (pt2cm(pt10A), pt2cm((pt0[0], pt0[1] + 0.75)), pt2cm(pt11))
pf_10A_11 = fitpath(p, 10e-1)
sp_10A_11 = pathtosvg(pf_10A_11)
seg_10A_11 = parse_path(sp_10A_11)
t = seg_10A_11.ilength(4.0 * cm)
pt10B = pt("10B", pt10A[0] - abs(pt10A[0] - np.real(seg_10A_11.point(t)) / cm),
           (np.imag(seg_10A_11.point(t)) / cm))
#t = seg_10A_11.ilength(1.0*cm)
#pt10C = pt("10C", np.real(seg.point(t))/cm, np.imag(seg.point(t))/cm)

path1 = svgwrite.path.Path('%s' % sp_10A_11, fill="none",
                           stroke=col_sew)  # (pt2ph(pt10B),

# only curve 0.25, not 0.5
コード例 #2
0
def scan_lines(paths, current_y=None):
    bbox = overall_bbox(paths)
    lines = []
    fudge_factor = 0.01
    orientation = abs(bbox[3]-bbox[2]) > abs(bbox[1]-bbox[0])
    if not current_y:
        current_y = bbox[2] if orientation else bbox[0]
    max_pos = bbox[3] if orientation else bbox[1]
    debug_shapes = [[paths, "none", "gray"]]

    while current_y < max_pos:
        current_y += MINIMUM_STITCH_DISTANCE

        if orientation:
            left = min(bbox[0], bbox[1])
            right = max(bbox[0], bbox[1])

            if left < 0:
                left *= 1.0 + fudge_factor
            else:
                left *= 1.0 - fudge_factor
            if right < 0:
                right *= 1.0 - fudge_factor
            else:
                right *= 1.0 + fudge_factor
            test_line = Line(start=current_y*1j+left, end=current_y*1j+right)
        else:
            up = min(bbox[2], bbox[3])
            down = max(bbox[2], bbox[3])
            if up < 0:
                up *= 1.0 + fudge_factor
            else:
                up *= 1.0 - fudge_factor
            if down < 0:
                down *= 1.0 - fudge_factor
            else:
                down *= 1.0 + fudge_factor
            test_line = Line(start=current_y  + up*1j,
                             end=current_y + down *1j)
        squash_intersections = []
        for path in paths:
            if path.start == path.end:
                continue
            intersections = path.intersect(test_line)
            if len(intersections) > 0:
                squash_intersections += [test_line.point(p[1]) for p in intersections]
        if len(squash_intersections) == 0:
            continue

        intersections = sorted(squash_intersections, key=lambda x: abs(x-test_line.start))
        if len(squash_intersections) < 2:
            continue
        debug_shapes.append([test_line, "none", "black"])
        for i in range(0, 2*int(len(intersections)/2), 2):
            def format_center(ind):
                return (intersections[ind].real, intersections[ind].imag)
            debug_shapes.append([Circle(center=format_center(i), r=1, fill="red")])
            debug_shapes.append([Circle(center=format_center(i+1), r=1, fill="blue")])
            line = Line(start=intersections[i], end=intersections[i+1])
            debug_shapes.append([line, "none", "green"])
            if line.length() > MAXIMUM_STITCH:
                num_segments = ceil(line.length() / MAXIMUM_STITCH)
                for seg_i in range(int(num_segments)):
                    lines.append(Line(start=line.point(seg_i/num_segments),
                                      end=line.point((seg_i+1)/num_segments)))
            else:
                lines.append(line)
        write_debug("fillscan", debug_shapes)
    return lines
コード例 #3
0
    def fill_polygon(self, paths):
        rotated = 0
        fudge_factor = 0.03
        while len(paths) > 2:
            if len(paths) < 4:
                self.fill_triangle(paths, color="red")
                return
            shapes = [[Path(*paths), "none", "blue"],
                      [Path(*paths), "none", "green"]]
            write_debug("close", shapes)
            paths = remove_close_paths(paths)

            if len(paths) <= 2:
                return
            # check whether the next triangle is concave
            test_line1 = Line(start=paths[0].start, end=paths[1].end)
            test_line1 = Line(start=test_line1.point(fudge_factor),
                              end=test_line1.point(1 - fudge_factor))
            comparison_path = Path(*paths)
            if test_line1.length() == 0:
                has_intersection = True
            else:
                has_intersection = len([
                    1 for line in paths if len(line.intersect(test_line1)) > 0
                ]) > 0

            if not path1_is_contained_in_path2(
                    test_line1, comparison_path) or has_intersection:
                shapes = [[comparison_path, "none", "blue"],
                          [test_line1, "none", "black"]]
                write_debug("anim", shapes)
                # rotate the paths
                paths = paths[1:] + [paths[0]]
                rotated += 1
                if rotated >= len(paths):
                    print("failed to rotate into a concave path -> ",
                          (test_line1.start.real, test_line1.start.imag),
                          (test_line1.end.real, test_line1.end.imag),
                          [(p.start.real, p.start.imag) for p in paths])
                    return
                continue
            side = shorter_side(paths)

            test_line2 = Line(start=paths[1].start, end=paths[2].end)
            test_line2 = Line(start=test_line2.point(fudge_factor),
                              end=test_line2.point(1 - fudge_factor))
            test_line3 = Line(start=paths[-1 + side].end,
                              end=paths[(3 + side) % len(paths)].start)
            test_line3 = Line(start=test_line3.point(fudge_factor),
                              end=test_line3.point(1 - fudge_factor))

            num_intersections = []
            for path in comparison_path:
                if test_line3.length() == 0:
                    print("test line 3 is degenerate!")
                num_intersections += test_line3.intersect(path)
                num_intersections += test_line2.intersect(path)

            rect_not_concave = not path1_is_contained_in_path2(
                test_line2, comparison_path)

            # test for concavity. If concave, fill as triangle
            if is_concave(
                    paths) or len(num_intersections) > 0 or rect_not_concave:
                self.fill_triangle(paths, color="blue")
                shapes = [[Path(*paths), "none", "black"]]
                to_remove = []
                to_remove.append(paths.pop(0))
                to_remove.append(paths.pop(0))
                for shape in to_remove:
                    shapes.append([shape, "none", "blue"])
                closing_line = Line(start=paths[-1].end, end=paths[0].start)
                shapes.append([closing_line, "none", "green"])
                shapes.append([test_line1, "none", "red"])
                write_debug("rem", shapes)

            else:
                # check whether the next triangle is concave
                side, side2 = self.fill_trap(paths)
                if side:
                    paths = paths[1:] + [paths[0]]
                shapes = [[Path(*paths), "none", "black"]]
                to_remove = []
                to_remove.append(paths.pop(0))
                to_remove.append(paths.pop(0))
                to_remove.append(paths.pop(0))
                # if the trap was stitched in the vertical (perpendicular to the
                # stitches), don't remove that segment
                linecolors = ["blue", "purple", "pink"]
                for i, shape in enumerate(to_remove):
                    shapes.append([shape, "none", linecolors[i]])
                closing_line = Line(start=paths[-1].end, end=paths[0].start)
                shapes.append([closing_line, "none", "green"])
                shapes.append([test_line2, "none", "purple"])
                write_debug("rem", shapes)
                delta = closing_line.length() - (test_line3.length() /
                                                 (1.0 - 2.0 * fudge_factor))
                if abs(delta) > 1e-14:
                    print("closing line different than test!", side,
                          test_line3, closing_line)
            rotated = 0
            if paths[-1].end != paths[0].start:
                # check for intersections
                closing_line = Line(start=paths[-1].end, end=paths[0].start)
                paths.insert(0, closing_line)
            else:
                print("removed paths but they connected anyway")
コード例 #4
0
ファイル: transects4rings.py プロジェクト: mathandy/svgtree
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
コード例 #5
0
ファイル: transects4rings.py プロジェクト: mathandy/svgtree
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
コード例 #6
0
def offset_curve(path, offset_distance, steps=200):
    """Takes in a Path object, `path`, and a distance,
    `offset_distance`, and outputs an piecewise-linear approximation
    of the 'parallel' offset curve."""
    nls = []
    if 'intersect' in locals():
        del intersect
    '''    
    for n, segX in enumerate(path):

        u1 = segX.unit_tangent(0.0)
        u2 = segX.unit_tangent(1.0)
        mag = 1.0*cm
        tan1 = Line(segX.point(0.0), segX.point(0.0) + mag*u1*-1).reversed()
        tan2 = Line(segX.point(1.0), segX.point(1.0) + mag*u2)
        for seg in tan1, segX, tan2:
            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)
    '''

    for n, segX in enumerate(path):
        if n == len(path) - 1:
            n = -1
        if 'intersect' in locals():
            segX = segX.cropped(intersect[1], 1)
            del intersect
        segXN9 = segX.normal(0.9) * offset_distance
        segXN1 = segX.normal(1.0) * offset_distance
        segYN0 = path[n + 1].normal(0.0) * offset_distance
        segYN1 = path[n + 1].normal(0.1) * offset_distance
        nlX = Line(segX.point(0.9) + segXN9, segX.point(1) + segXN1)
        nlY = Line(path[n + 1].point(0) + segYN0,
                   path[n + 1].point(0.1) + segYN1)

        #print nlX.intersect(nlY)
        if nlX.intersect(nlY):
            intersect = nlX.intersect(nlY)[0]
            seg = segX.cropped(0, intersect[0])
            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)
        else:
            nls_X = []
            nls_Y = []
            for k in range(50):
                t = k / float(50)
                offset_vector_X = offset_distance * segX.normal(t)
                nl = Line(segX.point(t), segX.point(t) + offset_vector_X)
                nls_X.append(nl)

                offset_vector_Y = offset_distance * path[n + 1].normal(t)
                nl = Line(path[n + 1].point(t),
                          path[n + 1].point(t) + offset_vector_Y)
                nls_Y.append(nl)

            connect_the_dots_X = [
                Line(nls_X[k].end, nls_X[k + 1].end)
                for k in range(len(nls_X) - 1)
            ]
            connect_the_dots_Y = [
                Line(nls_Y[k].end, nls_Y[k + 1].end)
                for k in range(len(nls_Y) - 1)
            ]

            for n1, x in enumerate(connect_the_dots_X):
                for m, y in enumerate(connect_the_dots_Y):
                    if x.intersect(y):
                        intersect = [n1 / 50., m / 50.]  #x.intersect(y)

            if 'intersect' in locals():
                seg = segX.cropped(0, intersect[0])
                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)

            else:
                u1 = segX.unit_tangent(1.0)
                u2 = path[n + 1].unit_tangent(0.0)
                mag = 3.0 * cm  # to ensure it will intersect
                tan1 = Line(segX.point(1.0), segX.point(1.0) + mag * u1)
                tan2 = Line(path[n + 1].point(0.0),
                            path[n + 1].point(0.0) + mag * u2 * -1).reversed()
                tan1N0 = tan1.normal(0) * offset_distance
                tan1N1 = tan1.normal(1.0) * offset_distance
                tan2N1 = tan2.normal(1) * offset_distance
                tan2N0 = tan2.normal(0) * offset_distance
                nlX = Line(tan1.point(0) + tan1N0, tan1.point(1) + tan1N1)
                nlY = Line(tan2.point(0) + tan2N0, tan2.point(1) + tan2N1)

                # calc angle
                a = np.array([np.real(tan1[1]), np.imag(tan1[1])])
                b = np.array([np.real(tan1[0]), np.imag(tan1[0])])
                c = np.array([np.real(tan2[0]), np.imag(tan2[0])])  # tan2[0]

                ba = a - b
                bc = c - b

                from math import acos
                from math import sqrt
                from math import pi

                def length(v):
                    return sqrt(v[0]**2 + v[1]**2)

                def dot_product(v, w):
                    return v[0] * w[0] + v[1] * w[1]

                def determinant(v, w):
                    return v[0] * w[1] - v[1] * w[0]

                def inner_angle(v, w):
                    cosx = dot_product(v, w) / (length(v) * length(w))
                    rad = acos(cosx)  # in radians
                    return rad * 180 / pi  # returns degrees

                def angle_clockwise(A, B):
                    inner = inner_angle(A, B)
                    det = determinant(A, B)
                    if det < 0:  #this is a property of the det. If the det < 0 then B is clockwise of A
                        return inner
                    else:  # if the det > 0 then A is immediately clockwise of B
                        return 360 - inner

                #cosine_angle = np.dot(bc, ba) / (np.linalg.norm(ba) * np.linalg.norm(bc))
                #angle = np.arccos(cosine_angle)
                # print angle_clockwise(bc, ba)
                if angle_clockwise(bc, ba) < 90.0:
                    seg = segX
                    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)

                elif len(nlX.intersect(nlY)) > 0:

                    intersectT = nlX.intersect(nlY)
                    #print intersectT
                    tan1 = tan1.cropped(0, intersectT[0][0])
                    tan2 = tan2.cropped(intersectT[0][1], 1)
                    #print intersectT

                    for seg in segX, tan1, tan2:
                        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)
                else:
                    seg = segX
                    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