def get_perpendicular_point(pt, bl, tl, tr, br):
    '''
    Return the point if it is inside the quad, else, return
    a point on the border of the quad.

    This function helps prevent the user from dragging crop handles
    outside the strip's area.
    '''

    intersects = intersect_point_quad_2d(
        pt, bl, tl, tr, br)

    if intersects:
        return pt

    elif pt.x <= bl.x and pt.y <= bl.y:
        return Vector(bl)
    elif pt.x <= tl.x and pt.y >= tl.y:
        return Vector(tl)
    elif pt.x >= tr.x and pt.y >= tr.y:
        return Vector(tr)
    elif pt.x >= br.x and pt.y <= br.y:
        return Vector(br)

    max_x = max([tr.x, br.x])
    min_x = min([tl.x, bl.x])

    max_y = max([tl.y, tr.y])
    min_y = min([bl.y, br.y])

    # pt left of left side
    if (pt.x <= tl.x or pt.x <= bl.x) and (pt.y >= bl.y and pt.y <= tl.y):
        right = Vector([max_x, pt.y])
        intersection = intersect_line_line_2d(bl, tl, pt, right)

    # pt right of right side
    elif (pt.x >= br.x or pt.x >= tr.x) and (pt.y >= br.y and pt.y <= tr.y):
        left = Vector([min_x, pt.y])
        intersection = intersect_line_line_2d(br, tr, pt, left)

    # pt above top side
    elif (pt.y >= tl.y or pt.y >= tr.y) and (pt.x >= tl.x and pt.x <= tr.x):
        bottom = Vector([pt.x, min_y])
        intersection = intersect_line_line_2d(tl, tr, pt, bottom)

    # pt below bottom side
    elif (pt.y <= bl.y or pt.y <= br.y) and (pt.x >= bl.x and pt.x <= br.x):
        top = Vector([pt.x, max_y])
        intersection = intersect_line_line_2d(bl, br, pt, top)

    return intersection
示例#2
0
def spline_X_shape(shape_ob, spl_index):
    """
    > shape_ob:     bezier shape object
    > spl_index:    spline number
    < returns list of intersections, occuring segment
    """

    shape_list = linear_spline_list(shape_ob)
    sl = interpolate_all_segments(shape_ob.data.splines[spl_index])
    cross = []

    # check for self-intersections first
    sl_length = 0
    for i in range(len(sl) - 1):
        for j in range(i + 2, len(sl) - 1):
            x = intersect_line_line_2d(sl[i], sl[i + 1], sl[j], sl[j + 1])
            if x != None:
                cross.append(
                    [x, i // shape_ob.data.splines[spl_index].resolution_u, sl_length + (x.to_3d() - sl[i]).length]
                )
        sl_length += (sl[i + 1] - sl[i]).length

    # then empty active spline from shapelist
    shape_list[spl_index] = []

    # check complete shape for intersection with active spline
    sl_length = 0
    for i in range(len(sl) - 1):
        for j, spl in enumerate(shape_list):
            for k, pt in enumerate(spl):
                for l in range(len(pt) - 1):
                    x = intersect_line_line_2d(sl[i], sl[i + 1], pt[l], pt[l + 1])
                    if x != None and not (
                        (sl[i] - pt[l]).length < PRECISION
                        or (sl[i] - pt[l + 1]).length < PRECISION
                        or (sl[i + 1] - pt[l]).length < PRECISION
                        or (sl[i + 1] - pt[l + 1]).length < PRECISION
                    ):
                        cross.append(
                            [
                                x,
                                i // shape_ob.data.splines[spl_index].resolution_u,
                                sl_length + (x.to_3d() - sl[i]).length,
                            ]
                        )
        sl_length += (sl[i + 1] - sl[i]).length

    cross = sorted([c for c in cross], key=lambda i: i[2])
    return [c[0:2] for c in cross]
示例#3
0
def spline_X_shape(shape_ob, spl_index):
    '''
    > shape_ob:     bezier shape object
    > spl_index:    spline number
    < returns list of intersections, occuring segment
    '''

    shape_list = linear_spline_list(shape_ob)
    sl = interpolate_all_segments(shape_ob.data.splines[spl_index])
    cross = []

    # check for self-intersections first
    sl_length = 0
    for i in range(len(sl) - 1):
        for j in range(i + 2, len(sl) - 1):
            x = intersect_line_line_2d(sl[i], sl[i + 1], sl[j], sl[j + 1])
            if x != None:
                cross.append([
                    x, i // shape_ob.data.splines[spl_index].resolution_u,
                    sl_length + (x.to_3d() - sl[i]).length
                ])
        sl_length += (sl[i + 1] - sl[i]).length

    # then empty active spline from shapelist
    shape_list[spl_index] = []

    # check complete shape for intersection with active spline
    sl_length = 0
    for i in range(len(sl) - 1):
        for j, spl in enumerate(shape_list):
            for k, pt in enumerate(spl):
                for l in range(len(pt) - 1):
                    x = intersect_line_line_2d(sl[i], sl[i + 1], pt[l],
                                               pt[l + 1])
                    if x != None and not (
                        (sl[i] - pt[l]).length < PRECISION or
                        (sl[i] - pt[l + 1]).length < PRECISION or
                        (sl[i + 1] - pt[l]).length < PRECISION or
                        (sl[i + 1] - pt[l + 1]).length < PRECISION):
                        cross.append([
                            x,
                            i // shape_ob.data.splines[spl_index].resolution_u,
                            sl_length + (x.to_3d() - sl[i]).length
                        ])
        sl_length += (sl[i + 1] - sl[i]).length

    cross = sorted([c for c in cross], key=lambda i: i[2])
    return [c[0:2] for c in cross]
示例#4
0
def point_inside_border(verts,border, scale, offset):

    min_x = min(border[0][0],border[1][0])
    max_x = max(border[0][0],border[1][0])
    min_y = min(border[0][1],border[2][1])
    max_y = max(border[0][1],border[2][1])

    nverts= len(verts)

    inside = False
    for v in verts :
        co = scale*Vector(v) + Vector(offset)
        if min_x<=co[0]<= max_x and min_y<=co[1]<= max_y :
            inside = True
            break

    if not inside :
        for i in range(0,nverts) :
            a = scale*Vector(verts[i-1]) + Vector(offset)
            b = scale*Vector(verts[i]) + Vector(offset)

            for j in range(0,4):
                c = border[j-1]
                d = border[j]
                if intersect_line_line_2d(a,b,c,d):
                    inside = True
                    break

    return inside
def islandIntersectUvIsland(source, target, SourceOffset):
    # Is 1 point in the box, inside the vertLoops
    edgeLoopsSource = source[6] # Pretend this is offset
    edgeLoopsTarget = target[6]

    # Edge intersect test
    for ed in edgeLoopsSource:
        for seg in edgeLoopsTarget:
            i = geometry.intersect_line_line_2d(seg[0],
                                                seg[1],
                                                SourceOffset+ed[0],
                                                SourceOffset+ed[1],
                                                )
            if i:
                return 1 # LINE INTERSECTION

    # 1 test for source being totally inside target
    SourceOffset.resize_3d()
    for pv in source[7]:
        if pointInIsland(pv+SourceOffset, target[0]):
            return 2 # SOURCE INSIDE TARGET

    # 2 test for a part of the target being totally inside the source.
    for pv in target[7]:
        if pointInIsland(pv-SourceOffset, source[0]):
            return 3 # PART OF TARGET INSIDE SOURCE.

    return 0 # NO INTERSECTION
def process_stroke_split_at_crossings(stroke):
    strokes = []
    stroke = list(stroke)
    l = len(stroke)
    cstroke = [stroke.pop()]
    while stroke:
        if not stroke[-1]:
            strokes.append(cstroke)
            stroke.pop()
            cstroke = [stroke.pop()]
            continue
        p0,p1 = cstroke[-1],stroke[-1]
        # see if p0-p1 segment crosses any other segment
        for i in range(len(stroke)-3):
            q0,q1 = stroke[i+0],stroke[i+1]
            if q0 is None or q1 is None: continue
            p = intersect_line_line_2d(p0,p1, q0,q1)
            if not p: continue
            if (p-p0).length < 0.000001 or (p-p1).length < 0.000001: continue
            # intersection!
            strokes.append(cstroke + [p])
            cstroke = [p]
            # note: inserting None to indicate broken stroke
            stroke = stroke[:i+1] + [p,None,p] + stroke[i+1:]
            break
        else:
            # no intersections!
            cstroke.append(stroke.pop())
    if cstroke: strokes.append(cstroke)
    return strokes
示例#7
0
def islandIntersectUvIsland(source, target, SourceOffset):
    # Is 1 point in the box, inside the vertLoops
    edgeLoopsSource = source[6]  # Pretend this is offset
    edgeLoopsTarget = target[6]

    # Edge intersect test
    for ed in edgeLoopsSource:
        for seg in edgeLoopsTarget:
            i = geometry.intersect_line_line_2d(seg[0],
                                                seg[1],
                                                SourceOffset + ed[0],
                                                SourceOffset + ed[1],
                                                )
            if i:
                return 1  # LINE INTERSECTION

    # 1 test for source being totally inside target
    SourceOffset.resize_3d()
    for pv in source[7]:
        if pointInIsland(pv + SourceOffset, target[0]):
            return 2  # SOURCE INSIDE TARGET

    # 2 test for a part of the target being totally inside the source.
    for pv in target[7]:
        if pointInIsland(pv - SourceOffset, source[0]):
            return 3  # PART OF TARGET INSIDE SOURCE.

    return 0  # NO INTERSECTION
示例#8
0
def process_stroke_split_at_crossings(stroke):
    strokes = []
    stroke = list(stroke)
    l = len(stroke)
    cstroke = [stroke.pop()]
    while stroke:
        if not stroke[-1]:
            strokes.append(cstroke)
            stroke.pop()
            cstroke = [stroke.pop()]
            continue
        p0, p1 = cstroke[-1], stroke[-1]
        # see if p0-p1 segment crosses any other segment
        for i in range(len(stroke) - 3):
            q0, q1 = stroke[i + 0], stroke[i + 1]
            if q0 is None or q1 is None: continue
            p = intersect_line_line_2d(p0, p1, q0, q1)
            if not p: continue
            if (p - p0).length < 0.000001 or (p - p1).length < 0.000001:
                continue
            # intersection!
            strokes.append(cstroke + [p])
            cstroke = [p]
            # note: inserting None to indicate broken stroke
            stroke = stroke[:i + 1] + [p, None, p] + stroke[i + 1:]
            break
        else:
            # no intersections!
            cstroke.append(stroke.pop())
    if cstroke: strokes.append(cstroke)
    return strokes
示例#9
0
 def checkpt(self, point2d):
     intersections = 0
     for i in range(len(self.poly_points)):
         pt1 = self.poly_points[i]
         pt2 = self.poly_points[(i + 1) % len(self.poly_points)]
         intersect = intersect_line_line_2d(Vector(point2d),
                                            Vector([point2d[0], 0]),
                                            Vector(pt1), Vector(pt2))
         if intersect != None:
             intersections += 1
     return intersections % 2 != 0
示例#10
0
def intersect_edges_2d(verts, edges, epsilon):
    '''Iterate through edges  and expose them to intersect_line_line_2d'''
    verts_in = [Vector(v) for v in verts]
    ed_lengths = [(verts_in[e[1]] - verts_in[e[0]]).length for e in edges]
    verts_out = verts
    edges_out = []
    ed_inter = [[] for e in edges]
    for e, d, i in zip(edges, ed_lengths, range(len(edges))):
        # if there is no intersections this will create a normal edge
        ed_inter[i].append([0.0, e[0]])
        ed_inter[i].append([d, e[1]])
        v1 = verts_in[e[0]]
        v2 = verts_in[e[1]]
        if d == 0:
            continue

        for e2, d2, j in zip(edges, ed_lengths, range(len(edges))):

            if i <= j or d2 == 0:
                continue
            if (e2[0] in e) or (e2[1] in e):
                continue

            v3 = verts_in[e2[0]]
            v4 = verts_in[e2[1]]
            vx = intersect_line_line_2d(v1, v2, v3, v4)
            if vx:
                d_to_1 = (vx - v1.to_2d()).length
                d_to_2 = (vx - v3.to_2d()).length

                new_id = len(verts_out)
                if ((vx.x, vx.y, v1.z)) in verts_out:
                    new_id = verts_out.index((vx.x, vx.y, v1.z))
                else:
                    if d_to_1 < epsilon:
                        new_id = e[0]
                    elif d_to_1 > d - epsilon:
                        new_id = e[1]
                    elif d_to_2 < epsilon:
                        new_id = e2[0]
                    elif d_to_2 > d2 - epsilon:
                        new_id = e2[1]
                    if new_id == len(verts_out):
                        verts_out.append((vx.x, vx.y, v1.z))
                
                # first item stores distance to origin, second the vertex id
                ed_inter[i].append([d_to_1, new_id])
                ed_inter[j].append([d_to_2, new_id])


    edges_out = edges_from_ed_inter(ed_inter)

    return verts_out, edges_out
def intersect_edges_2d(verts, edges, epsilon):
    '''Iterate through edges  and expose them to intersect_line_line_2d'''
    verts_in = [Vector(v) for v in verts]
    ed_lengths = [(verts_in[e[1]] - verts_in[e[0]]).length for e in edges]
    verts_out = verts
    edges_out = []
    ed_inter = [[] for e in edges]
    for e, d, i in zip(edges, ed_lengths, range(len(edges))):
        # if there is no intersections this will create a normal edge
        ed_inter[i].append([0.0, e[0]])
        ed_inter[i].append([d, e[1]])
        v1 = verts_in[e[0]]
        v2 = verts_in[e[1]]
        if d == 0:
            continue

        for e2, d2, j in zip(edges, ed_lengths, range(len(edges))):

            if i <= j or d2 == 0:
                continue
            if (e2[0] in e) or (e2[1] in e):
                continue

            v3 = verts_in[e2[0]]
            v4 = verts_in[e2[1]]
            vx = intersect_line_line_2d(v1, v2, v3, v4)
            if vx:
                d_to_1 = (vx - v1.to_2d()).length
                d_to_2 = (vx - v3.to_2d()).length

                new_id = len(verts_out)
                if ((vx.x, vx.y, v1.z)) in verts_out:
                    new_id = verts_out.index((vx.x, vx.y, v1.z))
                else:
                    if d_to_1 < epsilon:
                        new_id = e[0]
                    elif d_to_1 > d - epsilon:
                        new_id = e[1]
                    elif d_to_2 < epsilon:
                        new_id = e2[0]
                    elif d_to_2 > d2 - epsilon:
                        new_id = e2[1]
                    if new_id == len(verts_out):
                        verts_out.append((vx.x, vx.y, v1.z))
                
                # first item stores distance to origin, second the vertex id
                ed_inter[i].append([d_to_1, new_id])
                ed_inter[j].append([d_to_2, new_id])


    edges_out = edges_from_ed_inter(ed_inter)

    return verts_out, edges_out
示例#12
0
    def texture_intersects_others(texture_data, texture_slot, atlas_data):
        tex_fits_width = texture_slot.x + (
            texture_data.width + atlas_data.margin) < atlas_data.width
        tex_fits_height = texture_slot.y + (
            texture_data.height + atlas_data.margin) < atlas_data.height
        if tex_fits_width == False or tex_fits_height == False:
            return True
        for slot in atlas_data.texture_slots:
            if slot.texture_data != None and slot != texture_slot:

                r1 = []
                r1.append(texture_slot.x)
                r1.append(texture_slot.y)
                r1.append(texture_slot.x + texture_data.width)
                r1.append(texture_slot.y + texture_data.height)
                r2 = []
                r2.append(slot.x)
                r2.append(slot.y)
                r2.append(slot.x + slot.texture_data.width)
                r2.append(slot.y + slot.texture_data.height)

                points_a = []
                points_a.append(Vector((r1[0], r1[1])))
                points_a.append(Vector((r1[2], r1[1])))
                points_a.append(Vector((r1[2], r1[3])))
                points_a.append(Vector((r1[0], r1[3])))
                lines_a = [[points_a[0], points_a[1]],
                           [points_a[1], points_a[2]],
                           [points_a[2], points_a[3]],
                           [points_a[3], points_a[0]]]

                points_b = []
                points_b.append(Vector((r2[0], r2[1])))
                points_b.append(Vector((r2[2], r2[1])))
                points_b.append(Vector((r2[2], r2[3])))
                points_b.append(Vector((r2[0], r2[3])))
                lines_b = [[points_b[0], points_b[1]],
                           [points_b[1], points_b[2]],
                           [points_b[2], points_b[3]],
                           [points_b[3], points_b[0]]]

                for line_a in lines_a:
                    for line_b in lines_b:
                        i = intersect_line_line_2d(line_a[0], line_a[1],
                                                   line_b[0], line_b[1])
                        if i != None:
                            return True

        return False
示例#13
0
def get_intersections(p1, p2, shape_ob):
    '''
    > p1, p2:       line segment as pair of 2D vectors
    > shape_ob:     bezier shape object
    < list of intersection points
    '''

    il = []

    for spl in linear_spline_list(shape_ob):
        for i in range(0, len(spl)-1):
            point = intersect_line_line_2d(p2, p1, spl[i], spl[i+1])
            if point != None and (point-p1.to_2d()).length > PRECISION:
                il.append(point)
    return il
示例#14
0
def point_over_shape(point, verts, loops, outside_point=(-1, -1)):
    out = Vector(outside_point)
    pt = Vector(point)

    intersections = 0
    for loop in loops:
        for i, p in enumerate(loop):
            a = Vector(verts[loop[i - 1]])
            b = Vector(verts[p])

            if intersect_line_line_2d(pt, out, a, b):
                intersections += 1

    if intersections % 2 == 1:  #chek if the nb of intersection is odd
        return True
示例#15
0
    def segment_intersection(self, p1, p2):
        if not isinstance(p1, Vector):
            p1 = Vector(p1)
        if not isinstance(p2, Vector):
            p2 = Vector(p2)

        min_r = BIG_FLOAT
        nearest = None

        for v_i, v_j in self.edges:
            intersection = intersect_line_line_2d(p1, p2, v_i, v_j)
            if intersection is not None:
                r = (p1 - intersection).length
                if r < min_r:
                    nearest = intersection
                    min_r = r

        return nearest
示例#16
0
def point_inside_loop(verts, outside_point,mouse, scale, offset):
    nverts= len(verts)

    out = Vector(outside_point)

    pt = Vector(mouse)

    intersections = 0
    for i in range(0,nverts):
        a = scale*Vector(verts[i-1]) + Vector(offset)
        b = scale*Vector(verts[i]) + Vector(offset)
        if intersect_line_line_2d(pt,out,a,b):
            intersections += 1

    inside = False
    if intersections%2 == 1 : #chek if the nb of intersection is odd
        inside = True

    return inside
示例#17
0
def border_over_shape(border, verts, loops):
    for loop in loops:
        for i, p in enumerate(loop):
            a = Vector(verts[loop[i - 1]])
            b = Vector(verts[p])

            for j in range(0, 4):
                c = border[j - 1]
                d = border[j]
                if intersect_line_line_2d(a, b, c, d):
                    return True

    for point in verts:
        if point_inside_rectangle(point, border):
            return True

    for point in border:
        if point_over_shape(point, verts, loops):
            return True
示例#18
0
def point_inside_loop(loop, point, scale, offset):

    nverts = len(loop)

    #vectorize our two item tuple
    out = Vector(outside_loop(loop, scale, offset))
    pt = Vector(point)

    intersections = 0
    for i in range(0, nverts):
        a = scale * Vector(loop[i - 1]) + Vector(offset)
        b = scale * Vector(loop[i]) + Vector(offset)
        if intersect_line_line_2d(pt, out, a, b):
            intersections += 1

    inside = False
    if fmod(intersections, 2):
        inside = True

    return inside
示例#19
0
def point_inside_loop(loop, point, scale, offset):
        
    nverts = len(loop)
    
    #vectorize our two item tuple
    out = Vector(outside_loop(loop, scale, offset))
    pt = Vector(point)
    
    intersections = 0
    for i in range(0,nverts):
        a = scale*Vector(loop[i-1]) + Vector(offset)
        b = scale*Vector(loop[i]) + Vector(offset)
        if intersect_line_line_2d(pt,out,a,b):
            intersections += 1
    
    inside = False
    if fmod(intersections,2):
        inside = True
    
    return inside
示例#20
0
def point_inside_loop2d(loop, point):
    '''
    args:
    loop: list of vertices representing loop
        type-tuple or type-Vector
    point: location of point to be tested
        type-tuple or type-Vector

    return:
        True if point is inside loop
    '''
    #test arguments type
    if any(not v for v in loop): return False

    ptype = str(type(point))
    ltype = str(type(loop[0]))
    nverts = len(loop)

    if 'Vector' not in ptype:
        point = Vector(point)

    if 'Vector' not in ltype:
        for i in range(0, nverts):
            loop[i] = Vector(loop[i])

    #find a point outside the loop and count intersections
    out = Vector(outside_loop_2d(loop))
    intersections = 0
    for i in range(0, nverts):
        a = Vector(loop[i - 1])
        b = Vector(loop[i])
        if intersect_line_line_2d(point, out, a, b):
            intersections += 1

    inside = False
    if math.fmod(intersections, 2):
        inside = True

    return inside
示例#21
0
def point_inside_loop2d(loop, point):
    '''
    args:
    loop: list of vertices representing loop
        type-tuple or type-Vector
    point: location of point to be tested
        type-tuple or type-Vector
    
    return:
        True if point is inside loop
    '''    
    #test arguments type
    if any(not v for v in loop): return False
    
    ptype = str(type(point))
    ltype = str(type(loop[0]))
    nverts = len(loop)
    
    if 'Vector' not in ptype:
        point = Vector(point)
        
    if 'Vector' not in ltype:
        for i in range(0,nverts):
            loop[i] = Vector(loop[i])
        
    #find a point outside the loop and count intersections
    out = Vector(outside_loop_2d(loop))
    intersections = 0
    for i in range(0,nverts):
        a = Vector(loop[i-1])
        b = Vector(loop[i])
        if intersect_line_line_2d(point,out,a,b):
            intersections += 1
    
    inside = False
    if math.fmod(intersections,2):
        inside = True
    
    return inside
示例#22
0
文件: objects.py 项目: y-vas/game
    def cut(self):
        v3 = ( 0, 0, 0 )
        v4 = ( 0, self.size * 2 , 0 )

        self.verts.append( v3 )
        self.verts.append( v4 )

        vl = len( self.verts )

        # self.edges.append( (vl-2,vl-1))

        everts = []

        # print(v3,v4)
        for edge in self.edges:
            # print(edge)

            v1 = self.verts[edge[0]]
            v2 = self.verts[edge[1]]

            print('-' * 20)
            print( v1, v2 )
            ins = intersect_line_line_2d(v1,v2,v3,v4)

            if ins != None:
                everts.append( ( ins.x, ins.y , 0) )


        for x in everts:
            self.verts.append(x)

            vs = len( self.verts ) - 1
            print(self.verts[vs])
            print(self.verts[vl-2])
            print(x)

            self.edges.append( ( vl-2, vs ) )
示例#23
0
def process_stroke_get_next(stroke, from_edge, edges2D):
    # returns the next chunk of stroke to be processed
    # stops at...
    # - discontinuity
    # - intersection with self
    # - intersection with edges (ignoring from_edge)
    # - "strong" corners

    cstroke = []
    to_edge = None
    curve_distance, curve_threshold = 25.0, math.cos(60.0 * math.pi / 180.0)
    discontinuity_distance = 10.0

    def compute_cosangle_at_index(idx):
        nonlocal stroke
        if idx >= len(stroke): return 1.0
        p0 = stroke[idx]
        for iprev in range(idx - 1, -1, -1):
            pprev = stroke[iprev]
            if (p0 - pprev).length < curve_distance: continue
            break
        else:
            return 1.0
        for inext in range(idx + 1, len(stroke)):
            pnext = stroke[inext]
            if (p0 - pnext).length < curve_distance: continue
            break
        else:
            return 1.0
        dprev = (p0 - pprev).normalized()
        dnext = (pnext - p0).normalized()
        cosangle = dprev.dot(dnext)
        return cosangle

    for i0 in range(1, len(stroke) - 1):
        i1 = i0 + 1
        p0, p1 = stroke[i0], stroke[i1]

        # check for discontinuity
        if (p0 - p1).length > discontinuity_distance:
            # dprint('frag: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
            pass
            return (from_edge, stroke[:i1], None, False, stroke[i1:])

        # check for self-intersection
        for j0 in range(i0 + 3, len(stroke) - 1):
            q0, q1 = stroke[j0], stroke[j0 + 1]
            p = intersect_line_line_2d(p0, p1, q0, q1)
            if not p: continue
            # dprint('self: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
            pass
            return (from_edge, stroke[:i1], None, False, stroke[i1:])

        # check for intersections with edges
        for bme, (q0, q1) in edges2D:
            if bme is from_edge: continue
            p = intersect_line_line_2d(p0, p1, q0, q1)
            if not p: continue
            # dprint('edge: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
            pass
            return (from_edge, stroke[:i1], bme, True, stroke[i1:])

        # check for strong angles
        cosangle = compute_cosangle_at_index(i0)
        if cosangle > curve_threshold: continue
        # found a strong angle, but there may be a stronger angle coming up...
        minangle = cosangle
        for i0_plus in range(i0 + 1, len(stroke)):
            p0_plus = stroke[i0_plus]
            if (p0 - p0_plus).length > curve_distance: break
            minangle = min(compute_cosangle_at_index(i0_plus), minangle)
            if minangle < cosangle: break
        if minangle < cosangle: continue
        # dprint('bend: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
        pass
        return (from_edge, stroke[:i1], None, False, stroke[i1:])

    # dprint('full: %d %d' % (len(stroke), len(stroke)))
    pass
    return (from_edge, stroke, None, False, [])
示例#24
0
def in_frame(cam, obj, container):
    ''' Filter objs and return just those viewed from cam '''
    #print('Check visibility for', obj.name)
    linked = False
    if container.instance_collection:
        for inner_obj in container.instance_collection.all_objects:
            if obj == inner_obj:
                linked = True
                break
    if linked:
        print(obj.name, 'is linked')
        matrix = container.matrix_world
        #print('matrix', matrix)
        #print('obj matrix', obj.matrix_world)
        ref_offset = container.instance_collection.instance_offset
    else:
        matrix = Matrix(((1.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0),
                         (0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 0.0, 1.0)))
        ref_offset = Vector((0.0, 0.0, 0.0))
    box = [
        matrix @ ((obj.matrix_world @ Vector(v)) - ref_offset)
        for v in obj.bound_box
    ]
    #print('ref_offset', ref_offset)
    #print('box', box)

    frontal = False
    behind = False
    framed = False

    bound_box_verts_from_cam = []
    ## If a vertex of bounding box is in camera_view then object is in
    for v in box:
        x, y, z = world_to_camera_view(bpy.context.scene, cam, v)
        bound_box_verts_from_cam.append((x, y, z))
        if z >= cam.data.clip_start:
            frontal = True
        else:
            behind = True
        if 1 >= x >= 0 and 1 >= y >= 0:
            #print(obj.name, "is FRAMED! (in vertex", v, ")")
            framed = True

    if not framed:
        ## Check if obect is bigger than frame
        for face in BOUNDING_BOX_FACES:
            face_verts = [bound_box_verts_from_cam[v] for v in face]
            intersect = mathutils.geometry.intersect_point_quad_2d(
                Vector((0.5, 0.5, 0.0)), face_verts[0], face_verts[1],
                face_verts[2], face_verts[3])
            if intersect:
                framed = True
                break

    if framed:
        return {'framed': framed, 'frontal': frontal, 'behind': behind}

    ## If an edge intersects camera frame then obj is in
    for e in BOUNDING_BOX_EDGES:
        box_edge = [
            Vector(
                world_to_camera_view(bpy.context.scene, cam, box[e[0]])[:2]),
            Vector(
                world_to_camera_view(bpy.context.scene, cam, box[e[1]])[:2])
        ]
        for i in range(4):
            intersect = geometry.intersect_line_line_2d(
                box_edge[0], box_edge[1], FRAME_EDGES[i],
                FRAME_EDGES[(i + 1) % 4])
            #print('intersect in', intersect)
            if intersect:
                #print(obj.name, "is FRAMED! (intersects in", intersect, ")")
                framed = True
                return {'framed': framed, 'frontal': frontal, 'behind': behind}

    #print(obj.name, "is NOT FRAMED!")
    frontal = False
    behind = False
    return {'framed': framed, 'frontal': frontal, 'behind': behind}
示例#25
0
    def execute(self, context):
        start = time.time()

        # Load the bmesh from current edit mode object
        obj = bpy.context.object
        bm = bmesh.from_edit_mesh(obj.data)
        face = bm.faces.active  # TODO: Allow selecting multiple faces

        # Compute target length of each edge
        poly = Polygon([v.co for v in face.verts])
        targetlen = self.edge_length * poly.average_edge_length() / 100.0

        # Check if there are any overly long edges that we should subdivide
        for edge in list(face.edges):
            length = (edge.verts[0].co - edge.verts[1].co).magnitude
            cuts = round(length / targetlen) - 1
            if cuts >= 1:
                bmesh.ops.subdivide_edges(bm, edges=[edge], cuts=cuts)

        # Cast the face into 2D coordinate space
        poly = Polygon([v.co for v in face.verts])

        # Build a mapping from resulting 2D points back to the original vertices.
        # We need this when building 3D faces later.
        vertidx = dict((id(poly.points[i]), face.verts[i])
                       for i in range(len(face.verts)))

        print("Loading: %6.3f s" % (time.time() - start))
        start = time.time()

        # Generate vertices at each triangle grid intersection inside the polygon
        newpoints = []
        locationidx = {}
        xstepsize = targetlen
        ystepsize = math.sqrt(3) / 2 * xstepsize
        ysteps = math.ceil((poly.max_y - poly.min_y) / ystepsize)
        xsteps = math.ceil((poly.max_x - poly.min_x) / xstepsize)
        offsety = ((poly.max_y - poly.min_y) - (ysteps * ystepsize)) / 2
        offsetx = ((poly.max_x - poly.min_x) - (xsteps * xstepsize)) / 2
        for yidx in range(ysteps):
            y = poly.min_y + offsety + (yidx + 0.5) * ystepsize
            for xidx in range(xsteps):
                mxidx = xidx * 2 + (1 - yidx % 2)
                x = poly.min_x + offsetx + mxidx * xstepsize / 2

                p = Vector([x, y])
                if poly.contains(
                        p) and poly.edge_distance(p)[1] > xstepsize / 4:
                    locationidx[(mxidx, yidx)] = p
                    newpoints.append(p)

        # Index from point id() to point instance.
        # To make it possible to ignore duplicate edges, the items must be hashable, so
        # the code above uses id() of points.
        # Perhaps freezing the vectors could work also.
        allpoints = newpoints + poly.points
        pointidx = dict((id(p), p) for p in allpoints)
        orig_edges = {
            sortedge((id(p1), id(p2)))
            for p1, p2 in iter_tuples(poly.points, 2)
        }

        # Generate faces and edges for the regularly spaced inner area.
        regular_edges = set()
        regular_faces = set()
        for xidx, yidx in locationidx.keys():
            # Check the triangles starting in +Y and -Y directions from here
            # p1--p2
            #  \ /
            #   c
            #  / \
            # p3--p4

            p1 = locationidx.get((xidx - 1, yidx - 1))
            p2 = locationidx.get((xidx + 1, yidx - 1))
            c = locationidx.get((xidx, yidx))
            p3 = locationidx.get((xidx - 1, yidx + 1))
            p4 = locationidx.get((xidx + 1, yidx + 1))

            if p1 and p2 and c:
                regular_edges |= {
                    sortedge((id(p1), id(p2))),
                    sortedge((id(p2), id(c))),
                    sortedge((id(c), id(p1)))
                }
                regular_faces |= {sortface(pointidx, (id(p1), id(p2), id(c)))}

            if p3 and p4 and c:
                regular_edges |= {
                    sortedge((id(p3), id(p4))),
                    sortedge((id(p4), id(c))),
                    sortedge((id(c), id(p3)))
                }
                regular_faces |= {sortface(pointidx, (id(p3), id(p4), id(c)))}

        # Figure out which vertices lie outside the regular inner area
        edgecounts = dict((id(p), 0) for p in newpoints)
        for p1, p2 in regular_edges:
            edgecounts[p1] += 1
            edgecounts[p2] += 1
        borderpoints = {p for p, c in edgecounts.items() if c < 6}
        regular_edges_on_border = {
            e
            for e in regular_edges
            if e[0] in borderpoints and e[1] in borderpoints
        }

        print("Regulars: %6.3f s" % (time.time() - start))
        start = time.time()

        # Connecting the border area vertices:
        # Generate edges between vertices that are close enough to each other
        border_edge_candidates = set()
        for p1 in [id(p) for p in poly.points]:
            for p2 in borderpoints:
                newedge = sortedge((p1, p2))
                if newedge not in regular_edges and newedge not in orig_edges:
                    if (pointidx[p1] -
                            pointidx[p2]).magnitude <= targetlen * 2:
                        border_edge_candidates.add(newedge)

        # When edges intersect each other, keep only the shortest edge.
        discardededges = set()
        border_edge_candidates = list(border_edge_candidates)
        for i, e1 in enumerate(border_edge_candidates):
            for e2 in regular_edges_on_border:
                if e1[0] not in e2 and e1[1] not in e2:
                    points = [
                        pointidx[e1[0]], pointidx[e1[1]], pointidx[e2[0]],
                        pointidx[e2[1]]
                    ]
                    if geometry.intersect_line_line_2d(*points):
                        # Always favor the regular edges
                        discardededges.add(e1)
                        break

            if e1 not in discardededges:
                for e2 in border_edge_candidates[i + 1:]:
                    if (e1[0] not in e2) and (e1[1] not in e2) and (
                            e2 not in discardededges):
                        points = [
                            pointidx[e1[0]], pointidx[e1[1]], pointidx[e2[0]],
                            pointidx[e2[1]]
                        ]
                        if geometry.intersect_line_line_2d(*points):
                            # Keep shorter one
                            d1 = (pointidx[e1[0]] - pointidx[e1[1]]).magnitude
                            d2 = (pointidx[e2[0]] - pointidx[e2[1]]).magnitude
                            if d1 < d2:
                                discardededges.add(e2)
                            else:
                                discardededges.add(e1)
        border_edges = {
            e
            for e in border_edge_candidates if e not in discardededges
        }

        print("Border edges: %6.3f s" % (time.time() - start))
        start = time.time()

        # Build a lookup from point id() to edge tuples
        # It allows us to find all edges that originate from a given point.
        alledges = regular_edges | border_edges | orig_edges
        edgelookup = dict((id(p), set()) for p in allpoints)
        for e in alledges:
            for p in e:
                edgelookup[p].add(e)

        # Take one edge at a time, and then try to find two other edges that
        # share points. Once such a triplet is found, make a triangular face
        # out of it.
        border_faces = set()
        for e1 in border_edges:
            e2_candidates = [e for e in edgelookup[e1[0]] if e1 != e]
            e3_candidates = [e for e in edgelookup[e1[1]] if e1 != e]
            for e2 in e2_candidates:
                p3 = e2[0] if e2[0] != e1[0] else e2[1]
                e3 = [e for e in e3_candidates if p3 in e]
                if e3:
                    border_faces.add(sortface(pointidx, (e1[0], e1[1], p3)))

        print("Border faces: %6.3f s" % (time.time() - start))
        start = time.time()

        # Add all new points to the bmesh as 3D vertices.
        # Update the vertidx mapping also.
        for p in newpoints:
            vertidx[id(p)] = bm.verts.new(poly.plane.to_3d(p))

        if regular_faces or border_faces:
            # Remove the original selected face
            bmesh.ops.delete(bm, geom=[bm.faces.active], context=3)

        if self.only_edges:
            # For debugging: show the edges instead of faces
            for p1, p2 in border_edges | regular_edges:
                print("Adding " + str((p1, p2)))
                bm.edges.new((vertidx[p1], vertidx[p2]))
        else:
            # Add all the new faces by looking up 3d vertices using the map.
            for p1, p2, p3 in (regular_faces | border_faces):
                f = bm.faces.new((vertidx[p1], vertidx[p2], vertidx[p3]))
                f.normal_update()

        bmesh.update_edit_mesh(obj.data)

        print("Storage: %6.3f s" % (time.time() - start))
        start = time.time()
        return {'FINISHED'}
示例#26
0
    def execute(self, context):
        start = time.time()

        # Load the bmesh from current edit mode object
        obj = bpy.context.object
        bm = bmesh.from_edit_mesh(obj.data)
        face = bm.faces.active  # TODO: Allow selecting multiple faces

        # Compute target length of each edge
        poly = Polygon([v.co for v in face.verts])
        targetlen = self.edge_length * poly.average_edge_length() / 100.0

        # Check if there are any overly long edges that we should subdivide
        for edge in list(face.edges):
            length = (edge.verts[0].co - edge.verts[1].co).magnitude
            cuts = round(length / targetlen) - 1
            if cuts >= 1:
                bmesh.ops.subdivide_edges(bm, edges=[edge], cuts=cuts)

        # Cast the face into 2D coordinate space
        poly = Polygon([v.co for v in face.verts])

        # Build a mapping from resulting 2D points back to the original vertices.
        # We need this when building 3D faces later.
        vertidx = dict((id(poly.points[i]), face.verts[i]) for i in range(len(face.verts)))

        print("Loading: %6.3f s" % (time.time() - start))
        start = time.time()

        # Generate vertices at each triangle grid intersection inside the polygon
        newpoints = []
        locationidx = {}
        xstepsize = targetlen
        ystepsize = math.sqrt(3) / 2 * xstepsize
        ysteps = math.ceil((poly.max_y - poly.min_y) / ystepsize)
        xsteps = math.ceil((poly.max_x - poly.min_x) / xstepsize)
        offsety = ((poly.max_y - poly.min_y) - (ysteps * ystepsize)) / 2
        offsetx = ((poly.max_x - poly.min_x) - (xsteps * xstepsize)) / 2
        for yidx in range(ysteps):
            y = poly.min_y + offsety + (yidx + 0.5) * ystepsize
            for xidx in range(xsteps):
                mxidx = xidx * 2 + (1 - yidx % 2)
                x = poly.min_x + offsetx + mxidx * xstepsize / 2

                p = Vector([x, y])
                if poly.contains(p) and poly.edge_distance(p)[1] > xstepsize / 4:
                    locationidx[(mxidx, yidx)] = p
                    newpoints.append(p)

        # Index from point id() to point instance.
        # To make it possible to ignore duplicate edges, the items must be hashable, so
        # the code above uses id() of points.
        # Perhaps freezing the vectors could work also.
        allpoints = newpoints + poly.points
        pointidx = dict((id(p), p) for p in allpoints)
        orig_edges = {sortedge((id(p1), id(p2))) for p1, p2 in iter_tuples(poly.points, 2)}

        # Generate faces and edges for the regularly spaced inner area.
        regular_edges = set()
        regular_faces = set()
        for xidx, yidx in locationidx.keys():
            # Check the triangles starting in +Y and -Y directions from here
            # p1--p2
            #  \ /
            #   c
            #  / \
            # p3--p4

            p1 = locationidx.get((xidx - 1, yidx - 1))
            p2 = locationidx.get((xidx + 1, yidx - 1))
            c = locationidx.get((xidx, yidx))
            p3 = locationidx.get((xidx - 1, yidx + 1))
            p4 = locationidx.get((xidx + 1, yidx + 1))

            if p1 and p2 and c:
                regular_edges |= {sortedge((id(p1), id(p2))),
                                  sortedge((id(p2), id(c))),
                                  sortedge((id(c), id(p1)))}
                regular_faces |= {sortface(pointidx, (id(p1), id(p2), id(c)))}

            if p3 and p4 and c:
                regular_edges |= {sortedge((id(p3), id(p4))),
                                  sortedge((id(p4), id(c))),
                                  sortedge((id(c), id(p3)))}
                regular_faces |= {sortface(pointidx, (id(p3), id(p4), id(c)))}

        # Figure out which vertices lie outside the regular inner area
        edgecounts = dict((id(p), 0) for p in newpoints)
        for p1, p2 in regular_edges:
            edgecounts[p1] += 1
            edgecounts[p2] += 1
        borderpoints = {p for p, c in edgecounts.items() if c < 6}
        regular_edges_on_border = {e for e in regular_edges
                                   if e[0] in borderpoints and e[1] in borderpoints}

        print("Regulars: %6.3f s" % (time.time() - start))
        start = time.time()

        # Connecting the border area vertices:
        # Generate edges between vertices that are close enough to each other
        border_edge_candidates = set()
        for p1 in [id(p) for p in poly.points]:
            for p2 in borderpoints:
                newedge = sortedge((p1, p2))
                if newedge not in regular_edges and newedge not in orig_edges:
                    if (pointidx[p1] - pointidx[p2]).magnitude <= targetlen * 2:
                        border_edge_candidates.add(newedge)

        # When edges intersect each other, keep only the shortest edge.
        discardededges = set()
        border_edge_candidates = list(border_edge_candidates)
        for i, e1 in enumerate(border_edge_candidates):
            for e2 in regular_edges_on_border:
                if e1[0] not in e2 and e1[1] not in e2:
                    points = [pointidx[e1[0]], pointidx[e1[1]], pointidx[e2[0]], pointidx[e2[1]]]
                    if geometry.intersect_line_line_2d(*points):
                        # Always favor the regular edges
                        discardededges.add(e1)
                        break

            if e1 not in discardededges:
                for e2 in border_edge_candidates[i + 1:]:
                    if (e1[0] not in e2) and (e1[1] not in e2) and (e2 not in discardededges):
                        points = [pointidx[e1[0]], pointidx[e1[1]], pointidx[e2[0]], pointidx[e2[1]]]
                        if geometry.intersect_line_line_2d(*points):
                            # Keep shorter one
                            d1 = (pointidx[e1[0]] - pointidx[e1[1]]).magnitude
                            d2 = (pointidx[e2[0]] - pointidx[e2[1]]).magnitude
                            if d1 < d2:
                                discardededges.add(e2)
                            else:
                                discardededges.add(e1)
        border_edges = {e for e in border_edge_candidates if e not in discardededges}

        print("Border edges: %6.3f s" % (time.time() - start))
        start = time.time()

        # Build a lookup from point id() to edge tuples
        # It allows us to find all edges that originate from a given point.
        alledges = regular_edges | border_edges | orig_edges
        edgelookup = dict((id(p), set()) for p in allpoints)
        for e in alledges:
            for p in e:
                edgelookup[p].add(e)

        # Take one edge at a time, and then try to find two other edges that
        # share points. Once such a triplet is found, make a triangular face
        # out of it.
        border_faces = set()
        for e1 in border_edges:
            e2_candidates = [e for e in edgelookup[e1[0]] if e1 != e]
            e3_candidates = [e for e in edgelookup[e1[1]] if e1 != e]
            for e2 in e2_candidates:
                p3 = e2[0] if e2[0] != e1[0] else e2[1]
                e3 = [e for e in e3_candidates if p3 in e]
                if e3:
                    border_faces.add(sortface(pointidx, (e1[0], e1[1], p3)))

        print("Border faces: %6.3f s" % (time.time() - start))
        start = time.time()

        # Add all new points to the bmesh as 3D vertices.
        # Update the vertidx mapping also.
        for p in newpoints:
            vertidx[id(p)] = bm.verts.new(poly.plane.to_3d(p))

        if regular_faces or border_faces:
            # Remove the original selected face
            bmesh.ops.delete(bm, geom=[bm.faces.active], context=3)

        if self.only_edges:
            # For debugging: show the edges instead of faces
            for p1, p2 in border_edges | regular_edges:
                print("Adding " + str((p1, p2)))
                bm.edges.new((vertidx[p1], vertidx[p2]))
        else:
            # Add all the new faces by looking up 3d vertices using the map.
            for p1, p2, p3 in (regular_faces | border_faces):
                f = bm.faces.new((vertidx[p1], vertidx[p2], vertidx[p3]))
                f.normal_update()

        bmesh.update_edit_mesh(obj.data)

        print("Storage: %6.3f s" % (time.time() - start))
        start = time.time()
        return {'FINISHED'}
示例#27
0
def intersect_line_tri_2d(v1, v2, tv1, tv2, tv3):
    """ 三角形と線分の交点を求める。 返り値のリストは0~2の長さ。 """
    vec1 = geom.intersect_line_line_2d(v1, v2, tv1, tv2)
    vec2 = geom.intersect_line_line_2d(v1, v2, tv2, tv3)
    vec3 = geom.intersect_line_line_2d(v1, v2, tv3, tv1)
    return [v for v in (vec1, vec2, vec3) if v is not None]
示例#28
0
    def _modal(self, context, event):
        dv = self.delta_vector()
        if self.x_key:
            dv.y = 0
        elif self.y_key:
            dv.x = 0

        global running_modals
        if running_modals:
            x_factor = 2 * pi / self.canvas_width
            y_factor = pi / self.canvas_height
        else:
            x_factor = .0025  #2*pi / 500
            y_factor = .0025  #pi / 250

        if self.z_key:
            self.light_handle.location.z = max(
                self.base_object_distance + dv.x * 0.05, 0)
            import bpy_extras
            start_pos = self.light_handle.matrix_world.to_translation(
            ) - self.profile_handle.location
            start_pos = start_pos.normalized(
            ) * context.space_data.clip_end + self.profile_handle.location
            self.z_start_position = bpy_extras.view3d_utils.location_3d_to_region_2d(
                context.region, context.space_data.region_3d, start_pos)
            if self.z_start_position is None:
                self.z_start_position = bpy_extras.view3d_utils.location_3d_to_region_2d(
                    context.region, context.space_data.region_3d,
                    self.light_handle.matrix_world.to_translation())
            self.z_end_position = bpy_extras.view3d_utils.location_3d_to_region_2d(
                context.region, context.space_data.region_3d,
                self.profile_handle.location)

            # self.z_start_position = bpy_extras.view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, self.light_handle.matrix_world.to_translation().normalized() * context.space_data.clip_end)
            # self.z_end_position = bpy_extras.view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, Vector((0,0,0)))

            if self.z_start_position is None or self.z_end_position is None:
                self.z_start_position = Vector((0, 0))
                self.z_end_position = Vector((0, 0))

            if running_modals:
                global panel_global
                v1 = panel_global.point_lt
                v2 = Vector((panel_global.point_rb.x, panel_global.point_lt.y))
                v3 = panel_global.point_rb
                v4 = Vector((panel_global.point_lt.x, panel_global.point_rb.y))
                lines = [(v1, v2), (v2, v3), (v3, v4), (v1, v4)]
                shortest = None
                for v1, v2 in lines:
                    intersection = intersect_line_line_2d(
                        self.z_start_position, self.z_end_position, v1, v2)
                    if intersection:
                        length = (self.z_start_position - intersection).length
                        if not shortest or length < shortest:
                            shortest = length
                            self.z_end_position = intersection

        else:
            self.light_actuator.rotation_euler = self.base_object_rotation.copy(
            )
            self.light_actuator.rotation_euler.x += dv.x * x_factor
            self.light_actuator.rotation_euler.y += dv.y * y_factor
            self.light_actuator.rotation_euler.y = clamp(
                -pi / 2 + 0.000001, self.light_actuator.rotation_euler.y,
                pi / 2 - 0.000001)

        bpy.context.workspace.status_text_set(
            f"Move Dx: {dv.x * x_factor:.3f} Dy: {dv.y * y_factor:.3f}   [X/Y] Axis  [Z] Distance  [Shift] Precision Mode"
        )
        #context.area.header_text_set(text=f"Move Dx: {dv.x * x_factor:.3f} Dy: {dv.y * y_factor:.3f}   [X/Y] Axis | [Shift] Precision Mode")

        if event.value == "PRESS" and not event.type in {
                "MOUSEMOVE", "INBETWEEN_MOUSEMOVE"
        }:
            return {"RUNNING_MODAL"}
        return {"PASS_THROUGH"}
def get_intersection_point(v_a1, v_a2, v_b1, v_b2):
    #return vector or None
    return intersect_line_line_2d(v_a1, v_a2, v_b1, v_b2)
def process_stroke_get_next(stroke, from_edge, edges2D):
    # returns the next chunk of stroke to be processed
    # stops at...
    # - discontinuity
    # - intersection with self
    # - intersection with edges (ignoring from_edge)
    # - "strong" corners
    
    cstroke = []
    to_edge = None
    curve_distance, curve_threshold = 25.0, math.cos(60.0 * math.pi/180.0)
    discontinuity_distance = 10.0
    
    def compute_cosangle_at_index(idx):
        nonlocal stroke
        if idx >= len(stroke): return 1.0
        p0 = stroke[idx]
        for iprev in range(idx-1, -1, -1):
            pprev = stroke[iprev]
            if (p0-pprev).length < curve_distance: continue
            break
        else:
            return 1.0
        for inext in range(idx+1, len(stroke)):
            pnext = stroke[inext]
            if (p0-pnext).length < curve_distance: continue
            break
        else:
            return 1.0
        dprev = (p0 - pprev).normalized()
        dnext = (pnext - p0).normalized()
        cosangle = dprev.dot(dnext)
        return cosangle
    
    for i0 in range(1, len(stroke)-1):
        i1 = i0 + 1
        p0,p1 = stroke[i0],stroke[i1]
        
        # check for discontinuity
        if (p0-p1).length > discontinuity_distance:
            dprint('frag: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
            return (from_edge, stroke[:i1], None, False, stroke[i1:])
        
        # check for self-intersection
        for j0 in range(i0+3, len(stroke)-1):
            q0,q1 = stroke[j0],stroke[j0+1]
            p = intersect_line_line_2d(p0,p1, q0,q1)
            if not p: continue
            dprint('self: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
            return (from_edge, stroke[:i1], None, False, stroke[i1:])
        
        # check for intersections with edges
        for bme,(q0,q1) in edges2D:
            if bme is from_edge: continue
            p = intersect_line_line_2d(p0,p1, q0,q1)
            if not p: continue
            dprint('edge: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
            return (from_edge, stroke[:i1], bme, True, stroke[i1:])
        
        # check for strong angles
        cosangle = compute_cosangle_at_index(i0)
        if cosangle > curve_threshold: continue
        # found a strong angle, but there may be a stronger angle coming up...
        minangle = cosangle
        for i0_plus in range(i0+1, len(stroke)):
            p0_plus = stroke[i0_plus]
            if (p0-p0_plus).length > curve_distance: break
            minangle = min(compute_cosangle_at_index(i0_plus), minangle)
            if minangle < cosangle: break
        if minangle < cosangle: continue
        dprint('bend: %d %d %d' % (i0, len(stroke), len(stroke)-i1))
        return (from_edge, stroke[:i1], None, False, stroke[i1:])
    
    dprint('full: %d %d' % (len(stroke), len(stroke)))
    return (from_edge, stroke, None, False, [])