Example #1
0
 def calc_rotation(self, curve, src, dst, plane=None):
     # Some hacks.
     # Problem: if src and/or dst are exactly parallel to
     # one of coordinate axes, then Vector.rotation_difference
     # sometimes returns a matrix that rotates our vector in
     # completely different plane.
     # For example, if whole figure lays in XY plane, and
     # we are trying to rotate src = (0, 1, 0) into dst = (1, 0, 0),
     # then rotation_difference might return us a matrix, which
     # rotates (-1, 0, 0) into (0, 0, -1), which is out of XY plane
     # ("because why no? it still rotates src into dst").
     # Solution (hack): if whole figure lays in XY plane, then do
     # not use general rotation_difference method, calculate
     # rotation along Z axis only.
     if plane == 'XY':
         # Another unobvious hack: Vector.angle_signed method
         # works with 2D vectors only (this is not stated in
         # it's documentation!). Fortunately, in this particular
         # case our vectors are actually 2D.
         dst = Vector((dst[0], dst[1]))
         src = Vector((src[0], src[1]))
         angle = dst.angle_signed(src)
         return Matrix.Rotation(angle, 4, 'Z')
     elif plane == 'YZ':
         dst = Vector((dst[1], dst[2]))
         src = Vector((src[1], src[2]))
         angle = dst.angle_signed(src)
         return Matrix.Rotation(angle, 4, 'X')
     elif plane == 'XZ':
         dst = Vector((dst[2], dst[0]))
         src = Vector((src[2], src[0]))
         angle = dst.angle_signed(src)
         return Matrix.Rotation(angle, 4, 'Y')
     else:
         return autorotate_diff(dst, src)
Example #2
0
 def calc_rotation(self, curve, src, dst, plane=None):
     # Some hacks.
     # Problem: if src and/or dst are exactly parallel to
     # one of coordinate axes, then Vector.rotation_difference
     # sometimes returns a matrix that rotates our vector in
     # completely different plane.
     # For example, if whole figure lays in XY plane, and
     # we are trying to rotate src = (0, 1, 0) into dst = (1, 0, 0),
     # then rotation_difference might return us a matrix, which
     # rotates (-1, 0, 0) into (0, 0, -1), which is out of XY plane
     # ("because why no? it still rotates src into dst").
     # Solution (hack): if whole figure lays in XY plane, then do
     # not use general rotation_difference method, calculate
     # rotation along Z axis only.
     if plane == 'XY':
         # Another unobvious hack: Vector.angle_signed method
         # works with 2D vectors only. Fortunately, in this particular
         # case our vectors are actually 2D.
         dst = Vector((dst[0], dst[1]))
         src = Vector((src[0], src[1]))
         angle = dst.angle_signed(src)
         return Matrix.Rotation(angle, 4, 'Z')
     elif plane == 'YZ':
         dst = Vector((dst[1], dst[2]))
         src = Vector((src[1], src[2]))
         angle = dst.angle_signed(src)
         return Matrix.Rotation(angle, 4, 'X')
     elif plane == 'XZ':
         dst = Vector((dst[2], dst[0]))
         src = Vector((src[2], src[0]))
         angle = dst.angle_signed(src)
         return Matrix.Rotation(angle, 4, 'Y')
     else:
         return autorotate_diff(dst, src)
Example #3
0
 def rounded_primitive(cls, verts, radius, resolution=2.0):
     if not verts: return
     if len(verts) == 1:
         yield from cls.arc(verts[0], radius, resolution, skip_end=1)
     elif len(verts) == 2:
         v0, v1 = verts
         dv = v1 - v0
         angle = Vector((0, 1)).angle_signed(Vector((-dv.y, dv.x)), 0.0)
         yield from cls.arc(v0, radius, resolution, angle - math.pi,
                            angle)
         yield from cls.arc(v1, radius, resolution, angle,
                            angle + math.pi)
     elif radius == 0:
         yield from verts  # exactly the same
     else:
         vref = Vector((0, 1))
         count = len(verts)
         for i0 in range(count):
             v0 = verts[i0]
             v1 = verts[(i0 + 1) % count]
             v2 = verts[(i0 + 2) % count]
             dv10 = v1 - v0
             dv21 = v2 - v1
             angle10 = vref.angle_signed(Vector((-dv10.y, dv10.x)), 0.0)
             angle21 = vref.angle_signed(Vector((-dv21.y, dv21.x)), 0.0)
             angle21 = angle10 + clamp_angle(angle21 - angle10)
             yield from cls.arc(v1, radius, resolution, angle10,
                                angle21)
Example #4
0
def svg_text_shader(item, text, mid, textCard, color,svg,parent=None):
    text_position = get_render_location(mid)
    svgColor = svgwrite.rgb(color[0]*255, color[1]*255, color[2]*255, '%')
    ssp1 = get_render_location(textCard[0])
    ssp2 = get_render_location(textCard[3])
    ssp3 = get_render_location(textCard[2])

    dirVec = Vector(ssp1) - Vector(ssp2)

    if dirVec.length == 0:
        return

    text_position  = (Vector(ssp1) + Vector(ssp3)) / 2 

    rotation = math.degrees(dirVec.angle_signed(Vector((1, 0))))
  
    parent.add(svg.text(text, insert=tuple(text_position), fill=svgColor, **{
            'transform': 'rotate({} {} {})'.format(
                rotation,
                text_position[0],
                text_position[1]
            ),
            'font-size': 12,
            'font-family': 'OpenGost Type B TT',
            'text-anchor': 'middle'
        }))
Example #5
0
    def autoscale(self):
        symmetry = nmv.physics.hex_symmetry_space if self.hex_mode else nmv.physics.symmetry_space

        for vert in self.bm.verts:
            u = Vector(self.field[vert.index])
            v = u.cross(vert.normal)
            ang = 0
            last_vec = u
            for loop in vert.link_loops:
                vert1 = loop.link_loop_next.vert
                vert2 = loop.link_loop_next.link_loop_next.vert
                if not last_vec:
                    vert1_vec = Vector(self.field[vert1.index])
                else:
                    vert1_vec = last_vec

                vert2_vec = nmv.physics.best_matching_vector(
                    symmetry(self.field[vert2.index], vert2.normal), vert1_vec)

                vert1_vec = Vector((vert1_vec.dot(u), vert1_vec.dot(v)))
                vert2_vec = Vector((vert2_vec.dot(u), vert2_vec.dot(v)))

                ang += vert1_vec.angle_signed(vert2_vec)
            self.scale[vert.index] = ang
        for i in range(20):
            self.scale += self.scale[self.walk_edges(0)]
            self.scale /= 2
        self.scale -= self.scale.min()
        self.scale /= self.scale.max()
    def execute(self,bgeCar):

        #vector between car and guide
        car_position = bgeCar.getPosition()
        guide_position = bgeCar.bgeGuide.getPosition()
        vec_to_guide = guide_position - car_position

        #car norm vector
        car_dir_vec = bgeCar.getVelocity()

        #in 2D
        vec_to_guide = Vector([vec_to_guide[0],vec_to_guide[1]])
        car_dir_vec = Vector([car_dir_vec[0],car_dir_vec[1]])
                
        #angle between the two
        angle = vec_to_guide.angle_signed(car_dir_vec,None)
        if angle==None:
            angleFormated = 0.0
        else:
            angleFormated = angle * 180/math.pi
        
        angle_treshold = 2.0
        
        if angleFormated > angle_treshold:
            bge.logic.sendMessage(bgeCar.carname + '_Left')
        elif angleFormated < -angle_treshold:
            bge.logic.sendMessage(bgeCar.carname + '_Right')
 def rounded_primitive(cls, verts, radius, resolution=2.0):
     if not verts: return
     if len(verts) == 1:
         yield from cls.arc(verts[0], radius, resolution, skip_end=1)
     elif len(verts) == 2:
         v0, v1 = verts
         dv = v1 - v0
         angle = Vector((0,1)).angle_signed(Vector((-dv.y, dv.x)), 0.0)
         yield from cls.arc(v0, radius, resolution, angle-math.pi, angle)
         yield from cls.arc(v1, radius, resolution, angle, angle+math.pi)
     elif radius == 0:
         yield from verts # exactly the same
     else:
         vref = Vector((0,1))
         count = len(verts)
         for i0 in range(count):
             v0 = verts[i0]
             v1 = verts[(i0 + 1) % count]
             v2 = verts[(i0 + 2) % count]
             dv10 = v1 - v0
             dv21 = v2 - v1
             angle10 = vref.angle_signed(Vector((-dv10.y, dv10.x)), 0.0)
             angle21 = vref.angle_signed(Vector((-dv21.y, dv21.x)), 0.0)
             angle21 = angle10 + clamp_angle(angle21 - angle10)
             yield from cls.arc(v1, radius, resolution, angle10, angle21)
Example #8
0
def angle_signed(
    a: Vector,
    b: Vector,
    c: Vector,
):
    v1 = a - b
    v2 = c - b

    return Vector.angle_signed(v1, v2)
Example #9
0
    def ang_2d_sort(x):
        pt1 = bm.verts[x].co
        vec = vm * wm * pt1 - vm * wm * pt0
        vec = Vector(vec[0:2])
        ang = vec.angle_signed(Vector((1, 0)))

        if ang < 0:
            return ang + 3.1415926 * 2
        else:
            return ang
Example #10
0
 def ang_2d_sort(x):
     pt1=bm.verts[x].co
     vec=vm*wm*pt1-vm*wm*pt0
     vec=Vector(vec[0:2])
     ang=vec.angle_signed(Vector((1,0)))
     
     if ang<0:
         return ang+3.1415926*2
     else:
         return ang
Example #11
0
def vector_rotation(vec):
    """
    Find vector rotation according to X axis.
    :arg vec: Input vector.
    :type vec: :class:'mathutils.Vector'
    :return: Angle in radians.
    :rtype: float
    """
    v0 = Vector((1, 0))
    ang = Vector.angle_signed(vec, v0)
    if ang < 0: ang = 2 * pi + ang
    return ang
 def tractorAimError(self):
     '''
     Return the angle between the vector the animal is
     facing, and the vector from the animal to the tractor.
     '''
     tractor = GameLogic.getCurrentScene().objects['tractor']
     p1 = self.worldPosition.copy()
     p1 = Vector((p1.x,p1.y))
     p2 = tractor.worldPosition.copy()
     p2 = Vector((p2.x,p2.y))
     v1 = self.orientation[:][1].copy()
     v1 = Vector((v1.x,v1.y*-1))
     v2 = p2 - p1
     return abs(v1.angle_signed(v2))
Example #13
0
    def detect_singularities(self):
        symmetry = nmv.physics.hex_symmetry_space if self.hex_mode else nmv.physics.symmetry_space
        cache = {}

        def symmetry_cached(vert):
            if vert in cache:
                return cache[vert]
            else:
                s = symmetry(self.field[vert.index], vert.normal)
                cache[vert] = s
                return s

        singularities = []

        if not self.hex_mode:
            for face in self.bm.faces:
                v0 = face.verts[0]
                v1 = face.verts[1]
                v2 = face.verts[2]
                vec0 = self.field[v0.index]
                vec1 = nmv.physics.best_matching_vector(
                    symmetry_cached(v1), vec0)
                v2_symmetry = symmetry_cached(v2)
                match0 = nmv.physics.best_matching_vector(v2_symmetry, vec0)
                match1 = nmv.physics.best_matching_vector(v2_symmetry, vec1)
                if match0.dot(match1) < 0.5:
                    singularities.append(face.calc_center_median())
        else:
            for vert in self.bm.verts:
                ang = 0
                u = nmv.physics.random_tangent_vector(vert.normal)
                v = u.cross(vert.normal)
                last_vec = None
                for loop in vert.link_loops:
                    vert1 = loop.link_loop_next.vert
                    vert2 = loop.link_loop_next.link_loop_next.vert
                    if not last_vec:
                        vert1_vec = symmetry_cached(vert1)[0]
                    else:
                        vert1_vec = last_vec
                    vert2_vec = nmv.physics.best_matching_vector(
                        symmetry_cached(vert2), vert1_vec)
                    last_vec = vert2_vec
                    vert1_vec = Vector((vert1_vec.dot(u), vert1_vec.dot(v)))
                    vert2_vec = Vector((vert2_vec.dot(u), vert2_vec.dot(v)))
                    ang += vert1_vec.angle_signed(vert2_vec)
                if ang > 0.9:
                    singularities.append(vert.co)

        self.singularities = singularities
Example #14
0
def bulge_to_arc(point, next, bulge):
    """
    point: start point of segment in lwpolyline
    next: end point of segment in lwpolyline
    bulge: number between 0 and 1
    Converts a bulge of lwpolyline to an arc with a bulge describing the amount of how much a straight segment should
    be bended to an arc. With the bulge one can find the center point of the arc that replaces the segment.
    """

    rot = Matrix(((0, -1, 0), (1, 0, 0), (0, 0, 1)))
    section = next - point
    section_length = section.length / 2
    direction = -bulge / abs(bulge)
    correction = 1
    sagitta_len = section_length * abs(bulge)
    radius = (sagitta_len**2 + section_length**2) / (2 * sagitta_len)
    if sagitta_len < radius:
        cosagitta_len = radius - sagitta_len
    else:
        cosagitta_len = sagitta_len - radius
        direction *= -1
        correction *= -1
    center = point + section / 2 + section.normalized(
    ) * cosagitta_len * direction @ rot
    cp = point - center
    cn = next - center
    cr = cp.to_3d().cross(cn.to_3d()) * correction
    start = Vector((1, 0))
    if cr[2] > 0:
        angdir = 0
        startangle = -start.angle_signed(cp.to_2d())
        endangle = -start.angle_signed(cn.to_2d())
    else:
        angdir = 1
        startangle = start.angle_signed(cp.to_2d())
        endangle = start.angle_signed(cn.to_2d())
    return ArcEntity(startangle, endangle, center.to_3d(), radius, angdir)
Example #15
0
def bulge_to_arc(point, next, bulge):
    """
    point: start point of segment in lwpolyline
    next: end point of segment in lwpolyline
    bulge: number between 0 and 1
    Converts a bulge of lwpolyline to an arc with a bulge describing the amount of how much a straight segment should
    be bended to an arc. With the bulge one can find the center point of the arc that replaces the segment.
    """

    rot = Matrix(((0, -1, 0), (1, 0, 0), (0, 0, 1)))
    section = next - point
    section_length = section.length / 2
    direction = -bulge / abs(bulge)
    correction = 1
    sagitta_len = section_length * abs(bulge)
    radius = (sagitta_len**2 + section_length**2) / (2 * sagitta_len)
    if sagitta_len < radius:
        cosagitta_len = radius - sagitta_len
    else:
        cosagitta_len = sagitta_len - radius
        direction *= -1
        correction *= -1
    center = point + section / 2 + section.normalized() * cosagitta_len * rot * direction
    cp = point - center
    cn = next - center
    cr = cp.to_3d().cross(cn.to_3d()) * correction
    start = Vector((1, 0))
    if cr[2] > 0:
        angdir = 0
        startangle = -start.angle_signed(cp.to_2d())
        endangle = -start.angle_signed(cn.to_2d())
    else:
        angdir = 1
        startangle = start.angle_signed(cp.to_2d())
        endangle = start.angle_signed(cn.to_2d())
    return ArcEntity(startangle, endangle, center.to_3d(), radius, angdir)
Example #16
0
def get_ninety_angle_from(a, b):
    '''Get angle from horizon bewteen 0 (->) and 90
    up      : -90
    horizon : 0
    down    : 90
    '''
    H = Vector((1, 0))
    #res = degrees( H.angle(b-a) )
    V = b - a
    if V == Vector((0, 0)):
        return
    angle = H.angle_signed(b - a)
    res = math.degrees(angle)

    if res > 90:
        res = res - 180
    if res < -90:
        res = res + 180
    return res
Example #17
0
def rotate_norms_mouse(modal, context, event, func_data):
    center = np.array(
        view3d_utils.location_3d_to_region_2d(modal.act_reg, modal.act_rv3d,
                                              modal._mode_cache[0]).to_3d())

    start_vec = Vector(modal._mouse_init - center).xy
    mouse_vec = Vector(modal._mouse_reg_loc - center).xy

    ang = mouse_vec.angle_signed(start_vec)
    if event.shift:
        ang *= 0.1

    if ang != 0.0:
        modal._mode_cache[
            1] = modal._mode_cache[1] + ang * modal._mode_cache[2]
        rotate_vectors(modal, modal._mode_cache[1])
        modal._mouse_init[:] = modal._mouse_reg_loc

        modal.redraw_active = True
    return
    def modal(self, context, event):
        context.area.tag_redraw()
        myobj = context.selected_objects[self.objIndex]
        annotation = myobj.AnnotationGenerator[0].annotations[self.idx]
        center = annotation.gizLoc
        region = bpy.context.region
        rv3d = bpy.context.space_data.region_3d
        center = view3d_utils.location_3d_to_region_2d(region, rv3d, center)
        #For some reason the value returned by view3d utils is 100px off in the y axis
        center += Vector((0, 100))
        vecLast = Vector((self.init_mouse_x, self.init_mouse_y))
        vecLast -= center
        delta = 0
        # Set Tweak Flags
        if event.ctrl:
            tweak_snap = True
        else:
            tweak_snap = False
        if event.shift:
            tweak_precise = True
        else:
            tweak_precise = False

        if event.type == 'MOUSEMOVE':
            sensitivity = 1

            vecDelta = Vector((event.mouse_x, event.mouse_y))
            vecDelta -= center
            delta += vecDelta.angle_signed(vecLast) * sensitivity

            delta = math.degrees(delta)
            if tweak_snap:
                delta = 5 * round(delta / 5)

            if self.constrainAxis[0]:
                annotation.annotationRotation[0] = self.init_x - math.radians(
                    delta)
                axisText = 'X: '
            if self.constrainAxis[1]:
                annotation.annotationRotation[1] = self.init_y + math.radians(
                    delta)
                axisText = 'Y: '
            if self.constrainAxis[2]:
                annotation.annotationRotation[2] = self.init_z - math.radians(
                    delta)
                axisText = 'Z: '

            vecLast = vecDelta
            context.area.header_text_set("Rotate " + axisText +
                                         "%.4f" % delta + "\u00b0")

        elif event.type == 'LEFTMOUSE':
            #Setting hide_viewport is a stupid hack to force Gizmos to update after operator completes
            context.area.header_text_set(None)
            bpy.context.window.cursor_modal_restore()
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            #Setting hide_viewport is a stupid hack to force Gizmos to update after operator completes
            context.area.header_text_set(None)
            bpy.context.window.cursor_modal_restore()
            annotation.annotationRotation[0] = self.init_x
            annotation.annotationRotation[1] = self.init_y
            annotation.annotationRotation[2] = self.init_z
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}
def svg_text_shader(item, text, mid, textCard, color, svg, parent=None):

    # Card Indicies:
    #
    #     1----------------2
    #     |                |
    #     |                |
    #     0----------------3

    text_position = get_render_location(mid)
    svgColor = svgwrite.rgb(color[0] * 100, color[1] * 100, color[2] * 100,
                            '%')
    ssp0 = get_render_location(textCard[0])
    ssp1 = get_render_location(textCard[1])
    ssp2 = get_render_location(textCard[2])
    ssp3 = get_render_location(textCard[3])

    cardHeight = Vector(ssp1) - Vector(ssp0)

    dirVec = Vector(ssp3) - Vector(ssp0)

    heightOffsetAmount = 1 / 8 * cardHeight.length
    heightOffset = Vector((dirVec[1], -dirVec[0])).normalized()

    heightOffset *= heightOffsetAmount

    leftVec = Vector(ssp0)
    rightVec = Vector(ssp3)
    midVec = (leftVec + rightVec) / 2

    if dirVec.length == 0:
        return

    text_position = (0, 0)
    text_anchor = 'start'
    if item.textAlignment == 'L':
        text_position = leftVec
        text_anchor = 'start'
        position_flip = rightVec
        anchor_flip = 'end'

    if item.textAlignment == 'C':
        text_position = midVec
        text_anchor = 'middle'
        position_flip = midVec
        anchor_flip = 'middle'

    if item.textAlignment == 'R':
        text_position = rightVec
        text_anchor = 'end'
        position_flip = leftVec
        anchor_flip = 'start'

    rotation = math.degrees(dirVec.angle_signed(Vector((1, 0))))
    if rotation > 90 or rotation < -90:
        rotation += 180
        #text_position = position_flip
        text_anchor = anchor_flip
        heightOffset = -heightOffset
        print('did flip')

    print(heightOffset)
    text_position += heightOffset
    view = get_view()
    res = bpy.context.scene.MeasureItArchProps.default_resolution
    if view is not None:
        res = view.res

    parent.add(
        svg.text(
            text,
            insert=tuple(text_position),
            fill=svgColor,
            **{
                'transform':
                'rotate({} {} {})'.format(rotation, text_position[0],
                                          text_position[1]),

                # I wish i could tell you why this fudge factor is necessary, but for some reason
                # spec-ing svg units in inches and using this factor for text size is the only way to get
                # sensible imports in both inkscape and illustrator
                'font-size':
                round(item.fontSize * 4.166666667 / (300 / res), 2),
                'font-family':
                'OpenSans Regular',
                'text-anchor':
                text_anchor,
                'text-align':
                text_anchor
            }))
Example #20
0
def vector_rotation(vec):
    v0 = Vector((1,0))
    ang = Vector.angle_signed(vec, v0)
    if ang < 0: ang = 2*pi + ang
    return ang
Example #21
0
def square_fit(context):
    obj = bpy.context.view_layer.objects.active
    bm = bmesh.from_edit_mesh(obj.data)

    uv_layer = bm.loops.layers.uv.verify()

    faces = list()

    #MAKE FACE LIST
    for face in bm.faces:
        if face.select:
            faces.append(face)

    #TEST IF QUADS OR NOT

    quadmethod = True
    #EXPERIMENTAL! TO MUCH SKEWING TEST:
    distorted = False

    for face in faces: 
        if len(face.loops) is not 4 :
            quadmethod = False
            #print('no quad!')

    #SLOW HERE, find faster way to test if selection is ring shaped

    #Unwrap and get the edge verts
    bmesh.update_edit_mesh(obj.data)
    bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
    bpy.ops.mesh.region_to_loop()

    obj = bpy.context.edit_object
    me = obj.data
    bm = bmesh.from_edit_mesh(me)

    edge_list = list()
    for e in bm.edges:
        if e.select is True:
            edge_list.append(e)
            #print(e)
            
    #select faces again
    for f in faces:
        f.select = True      
    #get start loop (this makes sure we loop in the right direction)
    startloop = None

    if(len(edge_list) == 0):
        print("weird! - means no mesh was sent?")
        return distorted 

    for l in edge_list[0].link_loops:
        if l.face.select is True:
            startloop = l
    #create sorted verts from start loop
    sorted_vert_list = list()
    for f in faces:
        f.select = False
    for e in edge_list:
        e.select = True

    sorted_vert_list.append(startloop.vert)
    startloop.edge.select = False
    sorted_vert_list.append(startloop.link_loop_next.vert)

    for i in range(1,len(edge_list)-1):
        #catch if a patch is donut shaped:
        if i >= len(sorted_vert_list):
            for f in faces:
                f.select = True
            bmesh.update_edit_mesh(obj.data)
            return False

        for e in sorted_vert_list[i].link_edges:
            if e.select is True:
                sorted_vert_list.append(e.other_vert(sorted_vert_list[i]))
                e.select = False

    #select faces again
    for f in faces:
        f.select = True

    #get UV
    sorted_uv_list = list()
    uv_layer = bm.loops.layers.uv.active
    for v in sorted_vert_list:
        for l in v.link_loops:
            if l.face.select is True:
                sorted_uv_list.append(l[uv_layer])
                break

    #get all angles
    sorted_angle_list = list()

    for i in range(len(sorted_uv_list)):
        prev = (i-1)%len(sorted_uv_list)
        next = (i+1)%len(sorted_uv_list)
        vector1 = Vector((sorted_uv_list[prev].uv.y-sorted_uv_list[i].uv.y,sorted_uv_list[prev].uv.x-sorted_uv_list[i].uv.x))
        vector2 = Vector((sorted_uv_list[next].uv.y-sorted_uv_list[i].uv.y,sorted_uv_list[next].uv.x-sorted_uv_list[i].uv.x))
        #check failcase of zero length vector:
        if vector1.length == 0 or vector2.length == 0:
            bmesh.update_edit_mesh(obj.data)
            return False
        angle = -math.degrees(vector1.angle_signed(vector2))
        if angle < 0:
            angle += 360
        sorted_angle_list.append(angle)

    
    #find concaves:
    for i in range(len(sorted_angle_list)):
        #print(sorted_angle_list[i])
        if sorted_angle_list[i] > 230:
            distorted = True
            bmesh.update_edit_mesh(obj.data)
            return False

    #angle test:
    #print("angles")
    #test if more than 4 90 degrees:
    NCount = 0
    for i in range(len(sorted_angle_list)):
        #print(sorted_angle_list[i])
        if sorted_angle_list[i] < 100:
            NCount += 1
    if NCount > 4:
        distorted = True

    
    if quadmethod: 
    #MAP FIRST QUAD
        edge1 = (faces[0].loops[0].vert.co.xyz - faces[0].loops[1].vert.co.xyz).length
        edge2 = (faces[0].loops[1].vert.co.xyz - faces[0].loops[2].vert.co.xyz).length

        faces[0].loops[0][uv_layer].uv.x = 0
        faces[0].loops[0][uv_layer].uv.y = 0
        faces[0].loops[1][uv_layer].uv.x = edge1
        faces[0].loops[1][uv_layer].uv.y = 0
        faces[0].loops[2][uv_layer].uv.x = edge1
        faces[0].loops[2][uv_layer].uv.y = edge2
        faces[0].loops[3][uv_layer].uv.x = 0
        faces[0].loops[3][uv_layer].uv.y = edge2

        bm.faces.active = faces[0]

        #UNWRAP ADJACENT
        bmesh.update_edit_mesh(obj.data)
        bpy.ops.uv.follow_active_quads()
        #print("quading it")


    if quadmethod is False:

        #now find top 4 angles
        topangles = list()
        for o in range(4):
            top = 360
            topindex = -1
            for i in range(len(sorted_angle_list)):
                if sorted_angle_list[i] < top:
                    top = sorted_angle_list[i]
                    topindex = i
            #print(sorted_angle_list[topindex])
            
            if o is 3:
                if sorted_angle_list[topindex] > 120:
                    distorted = True

            topangles.append(topindex)
            sorted_angle_list[topindex] = 999 #lol

        sorted_corner_list = list()
        for i in range(len(sorted_uv_list)):
            sorted_corner_list.append(False)
        sorted_corner_list[topangles[0]] = True
        sorted_corner_list[topangles[1]] = True
        sorted_corner_list[topangles[2]] = True
        sorted_corner_list[topangles[3]] = True

        #find bottom left corner (using distance method seems to work well)
        distance = 2
        closest = 0
        for t in topangles:
            l = sorted_uv_list[t].uv.length
            if l < distance:
                distance = l
                closest = t

        #rotate lists to get clostest corner at start:
        for i in range(closest):
            sorted_corner_list.append(sorted_corner_list.pop(0))
            sorted_uv_list.append(sorted_uv_list.pop(0))
            sorted_vert_list.append(sorted_vert_list.pop(0))

        sorted_edge_ratios = list()

        #get edge lenghts
        edge = list()
        for i in range(len(sorted_vert_list)):
            if sorted_corner_list[i] is True:
                sorted_edge_ratios.append(0)
                if i is not 0:
                    l = (sorted_vert_list[i-1].co.xyz - sorted_vert_list[i].co.xyz).length
                    edge.append(sorted_edge_ratios[i-1] + l)
                
            if sorted_corner_list[i] is False:
                l = (sorted_vert_list[i-1].co.xyz - sorted_vert_list[i].co.xyz).length
                sorted_edge_ratios.append(sorted_edge_ratios[i-1] + l)
            if i is (len(sorted_vert_list)-1):
                l = (sorted_vert_list[i].co.xyz - sorted_vert_list[0].co.xyz).length
                edge.append(sorted_edge_ratios[i] + l)

        if distorted is False:
            #NOW LAY OUT ALL EDGE UVs
            i = 0
            #EDGE 1
            for l in sorted_vert_list[i].link_loops:
                if l.face.select is True:
                    l[uv_layer].uv = Vector((0,0))
            i += 1
            while sorted_corner_list[i] is False:
                for l in sorted_vert_list[i].link_loops:
                    if l.face.select is True:
                        l[uv_layer].uv = Vector((sorted_edge_ratios[i]/edge[0],0)) 
                i += 1 
            #EDGE 2
            for l in sorted_vert_list[i].link_loops:
                if l.face.select is True:
                    l[uv_layer].uv = Vector((1,0))        
            i += 1
            while sorted_corner_list[i] is False:
                for l in sorted_vert_list[i].link_loops:
                    if l.face.select is True:
                        l[uv_layer].uv = Vector((1,sorted_edge_ratios[i]/edge[1])) 
                i += 1     
            #EDGE 3
            for l in sorted_vert_list[i].link_loops:
                if l.face.select is True:
                    l[uv_layer].uv = Vector((1,1))         
            i += 1
            while sorted_corner_list[i] is False:
                for l in sorted_vert_list[i].link_loops:
                    if l.face.select is True:
                        l[uv_layer].uv = Vector((1-(sorted_edge_ratios[i]/edge[2]),1)) 
                i += 1
            #EDGE 4
            for l in sorted_vert_list[i].link_loops:
                if l.face.select is True:
                    l[uv_layer].uv = Vector((0,1))
            i += 1
            for o in range(i,len(sorted_vert_list)):
                for l in sorted_vert_list[o].link_loops:
                    if l.face.select is True:
                        l[uv_layer].uv = Vector((0,1-(sorted_edge_ratios[o]/edge[3])))

            #set proper aspect ratio
        
            for f in bm.faces:
                if f.select is True:
                    for loop in f.loops:
                        loop_uv = loop[uv_layer]
                        loop_uv.uv.x *= edge[0]
                        loop_uv.uv.y *= edge[1]

        bmesh.update_edit_mesh(me, True)
        bpy.ops.uv.select_all(action='SELECT')
        bpy.ops.uv.minimize_stretch(iterations=100)   
        #return true if rect fit was succesful
        return not distorted