Example #1
0
    def debug_boundmesh(self, subsets, skeldata):
        vert_offset = 0
        for set, bone in zip(subsets, skeldata):
            if not set.faces:
                continue

            # Negate trans to account for flipped axes
            trans = Vector(bone.position)
            trans.negate()
            # Rotate rot quaternion to account for flipped axes
            rot = Quaternion(bone.rotation)
            rot.rotate( Quaternion((0,0,0,1)) )

            relative_zero = rot * trans
            verts = []
            faces = []
            for vert in set.vertices:
                # Relative vertex position: Absolute position - bone position
                # I guess?
                verts.append( rot * Vector(vert) + relative_zero )

            # Create object and mesh
            mesh = bpy.data.meshes.new(bone.name)

            object = bpy.data.objects.new(bone.name, mesh)
            bpy.context.scene.objects.link(object)
            bpy.context.scene.objects.active = object


            # Load vertices and faces
            mesh.from_pydata(verts, [], [])
Example #2
0
    def import_skeleton(self, data):
        skeldata = BoneData.build_bones(data)

        # Create armature and object
        name = 'Armature'
        bpy.ops.object.add(type='ARMATURE',
                           enter_editmode=True,
                           location=(0, 0, 0))
        # Armature object
        ob = bpy.context.active_object
        ob.show_in_front = True
        ob.name = name
        # Armature
        amt = ob.data
        amt.display_type = 'STICK'

        # Create bones from skeleton data
        # Sims 2 bones seem to be reversed head to tail
        for bonedata in skeldata:
            bone = amt.edit_bones.new(bonedata.name)

            # Negate trans to account for flipped axes
            trans = Vector(bonedata.position)
            trans.negate()
            # Rotate rot quaternion to account for flipped axes
            rot = Quaternion(bonedata.rotation)
            rot.rotate(Quaternion((0, 0, 0, 1)))

            bone.head = rot @ trans
            if bonedata.parent != None:
                parent = amt.edit_bones[bonedata.parent]
                bone.parent = parent
                bone.tail = parent.head
            # Check if the length of a bone is too short for blender
            bonelen = bone.tail.length - bone.head.length
            if bonelen > -0.0005 and bonelen < 0.0005:
                # Blender does not support 0 length bones
                bone.head += Vector((0, 0, 0.00005))

            # Enter custom properties for exporting later
            # # Translate Vector and Rotation Quaternion
            bone["translate"] = [
                bonedata.position[0], bonedata.position[1],
                bonedata.position[2]
            ]
            bone["rotation"] = [
                bonedata.rotation[0], bonedata.rotation[1],
                bonedata.rotation[2], bonedata.rotation[3]
            ]

        # Go back to Object mode
        bpy.ops.object.mode_set(mode='OBJECT')

        # Return the Armature object
        return ob
def spherize(context):
    print("in spherize")

    #influence
    influence = context.scene['unt_spherifyratio']

    #gets the location of the center of the center object
    if context.scene.centerobject:
        center = bpy.data.objects[context.scene.centerobject].location
    else:
        center = bpy.context.scene.cursor_location

    #get world matrix for the object
    worldmatrix = bpy.context.object.matrix_world

    ob = bpy.context.object
    obdata = ob.data

    #mandatory stupid step, calculate normals split, in object mode
    bpy.ops.object.mode_set(mode="OBJECT")

    obdata.calc_normals_split()

    #prepare a list for all the normals. One per "loop"(vertice-per-faace)
    normals = [Vector()] * len(obdata.loops)

    #loop all the loops (subvertices) in the mesh
    for loop in obdata.loops:
        #obdata.calc_normals_split()
        vertexindex = loop.vertex_index

        normals[loop.index] = loop.normal
        print("normal: %s" % normals[loop.index])

        #if the vertex is selected, normalize the normal
        if obdata.vertices[vertexindex].select:
            #get local coordinate of the related vertex
            localco = obdata.vertices[vertexindex].co
            #calculate the globla coordinates of the vertex
            globalco = worldmatrix * obdata.vertices[vertexindex].co
            #delta betwen the global location of the vertex and the center of the center object
            v = Vector([y - x for x, y in zip(globalco, center)])
            v.negate()
            v.normalize()
            #resulting vector (v*influence + normal*(1-influence))
            normals[loop.index] = v * float(influence) + normals[
                loop.index] * (float(1) - float(influence))
            #normals[loop.index].negate()
            normals[loop.index].normalize()
            print("new normal: %s" % normals[loop.index])

    obdata.normals_split_custom_set(normals)
    bpy.ops.object.mode_set(mode="EDIT")
def spherize(context):    
    print ("in spherize")
    
    #influence
    influence = context.scene['unt_spherifyratio']
    
    #gets the location of the center of the center object
    if context.scene.centerobject:
        center = bpy.data.objects[context.scene.centerobject].location
    else:
        center = bpy.context.scene.cursor_location
    
    #get world matrix for the object
    worldmatrix = bpy.context.object.matrix_world
    
    ob = bpy.context.object
    obdata = ob.data
    
    #mandatory stupid step, calculate normals split, in object mode
    bpy.ops.object.mode_set(mode="OBJECT")
    
    obdata.calc_normals_split()
    
    #prepare a list for all the normals. One per "loop"(vertice-per-faace)    
    normals = [Vector()]*len(obdata.loops)
    
    #loop all the loops (subvertices) in the mesh
    for loop in obdata.loops:
        #obdata.calc_normals_split()
        vertexindex = loop.vertex_index            

        normals[loop.index] = loop.normal
        print ("normal: %s"% normals[loop.index])
        
        #if the vertex is selected, normalize the normal            
        if obdata.vertices[vertexindex].select:
            #get local coordinate of the related vertex
            localco = obdata.vertices[vertexindex].co
            #calculate the globla coordinates of the vertex
            globalco = worldmatrix * obdata.vertices[vertexindex].co
            #delta betwen the global location of the vertex and the center of the center object
            v= Vector([y-x for x,y in zip(globalco,center)])
            v.negate()
            v.normalize()                             
            #resulting vector (v*influence + normal*(1-influence))
            normals[loop.index] = v * float(influence) + normals[loop.index] * (float(1)-float(influence))
            #normals[loop.index].negate()
            normals[loop.index].normalize()
            print ("new normal: %s"% normals[loop.index])
            
    obdata.normals_split_custom_set(normals)
    bpy.ops.object.mode_set(mode="EDIT")   
Example #5
0
def get_obj_axis(obj, axis):
    ax = 0
    if axis == 'Y' or axis == '-Y':
        ax = 1
    if axis == 'Z' or axis == '-Z':
        ax = 2

    obj_matrix = obj.matrix_world
    axis_tuple = (obj_matrix[0][ax], obj_matrix[1][ax], obj_matrix[2][ax])
    axisResult = Vector(axis_tuple).normalized()

    if axis == '-X' or axis == '-Y' or axis == '-Z':
        axisResult.negate()

    return axisResult
Example #6
0
def get_obj_axis(obj, axis):
    ax = 0
    if axis == "Y" or axis == "-Y":
        ax = 1
    if axis == "Z" or axis == "-Z":
        ax = 2

    obj_matrix = obj.matrix_world
    axis_tuple = (obj_matrix[0][ax], obj_matrix[1][ax], obj_matrix[2][ax])
    axisResult = Vector(axis_tuple).normalized()

    if axis == "-X" or axis == "-Y" or axis == "-Z":
        axisResult.negate()

    return axisResult
def get_obj_axis(obj, axis):
    ax = 0
    if axis == 'Y' or axis == '-Y':
        ax = 1
    if axis == 'Z' or axis == '-Z':
        ax = 2

    obj_matrix = obj.matrix_world
    axis_tuple = (
        obj_matrix[0][ax], obj_matrix[1][ax], obj_matrix[2][ax])
    axisResult = Vector(axis_tuple).normalized()

    if axis == '-X' or axis == '-Y' or axis == '-Z':
        axisResult.negate()

    return axisResult
Example #8
0
    def to_plane(self, amplitude, coefficient, vertex, centers, direction):
        center = Vector(centers[0])
        direction = Vector(direction)
        vertex = Vector(vertex)
        dirlength = direction.length
        if dirlength <= 0:
            raise ValueError("Direction vector must have nonzero length!")

        d = -direction.dot(center)
        # distance from vertex to plane
        rho = abs(vertex.dot(direction) + d) / dirlength

        from_center = center - vertex

        # vector is either direction or negative direction
        if from_center.dot(direction) >= 0:
            vector = direction.normalized()
        else:
            # for some reason mathutil's Vector does not have .negated()
            # thankfully we do not need direction itself anymore.
            direction.negate()
            vector = direction.normalized()

        return self.falloff(amplitude, coefficient, rho), vector
Example #9
0
    def execute(self, context):
        object = context.object
        object.update_from_editmode()

        self.pivot_point = context.space_data.pivot_point
        self.transform_orientation = context.space_data.transform_orientation

        object_bm = bmesh.from_edit_mesh(object.data)
        object_bm.verts.ensure_lookup_table()
        object_bm.edges.ensure_lookup_table()
        object_bm.faces.ensure_lookup_table()

        selected_faces = [f for f in object_bm.faces if f.select]
        selected_edges = [e for e in object_bm.edges if e.select]
        selected_verts = [v for v in object_bm.verts if v.select]

        if len(selected_edges) == 0:
            self.report({'WARNING'}, "Please select edges.")
            return {'CANCELLED'}

        try:
            cache_loops = get_cache(self.as_pointer(), "loops")
            loops = []
            for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in cache_loops:
                loops.append((([object_bm.verts[v] for v in loop_verts], [object_bm.edges[e] for e in loop_edges],
                               [object_bm.faces[f] for f in loop_faces]), is_loop_cyclic, is_loop_boundary))
        except CacheException:
            loops = get_loops(selected_edges, selected_faces)
            if loops:
                cache_loops = []
                for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in loops:
                    cache_loops.append((([v.index for v in loop_verts], [e.index for e in loop_edges],
                                         [f.index for f in loop_faces]), is_loop_cyclic, is_loop_boundary))
                set_cache(self.as_pointer(), "loops", cache_loops)

        if loops is None:
            self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).")
            return {'CANCELLED'}

        selection_center = Vector()
        for vert in selected_verts:
            selection_center += vert.co
        selection_center /= len(selected_verts)

        object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False)

        refresh_icons()
        shape_bm = bmesh.new()
        for loop_idx, ((loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary) in enumerate(loops):
            if len(loop_edges) < 3:
                continue
            loop_verts_len = len(loop_verts)

            shape_verts = None
            shape_edges = None
            try:
                cache_verts = get_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx))
                tmp_vert = None
                for i, cache_vert in enumerate(cache_verts):
                    new_vert = shape_bm.verts.new(cache_vert)
                    if i > 0:
                        shape_bm.edges.new((tmp_vert, new_vert))
                    tmp_vert = new_vert
                shape_verts = shape_bm.verts[:]
                shape_bm.edges.new((shape_verts[-1], shape_verts[0]))
                shape_edges = shape_bm.edges[:]

            except CacheException:
                if self.shape == "CIRCLE":
                    a = sum([e.calc_length() for e in loop_edges]) / loop_verts_len
                    diameter = a / (2 * math.sin(math.pi / loop_verts_len))
                    shape_segments = loop_verts_len + self.span
                    shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, radius=diameter/2)
                    shape_verts = shape_verts["verts"]
                    shape_edges = shape_bm.edges[:]

                elif self.shape == "RECTANGLE":
                    if loop_verts_len % 2 > 0:
                        self.report({'WARNING'}, "An odd number of edges.")
                        del shape_bm
                        return {'FINISHED'}
                    size = sum([e.calc_length() for e in loop_edges])

                    size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                    size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b
                    seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                    seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b)
                    if seg_a % 1 > 0:
                        self.report({'WARNING'}, "Incorrect sides ratio.")
                        seg_a += 1
                        seg_b += 2
                    seg_a = int(seg_a)
                    if self.is_square:
                        size_a = (size_a + size_b) / 2
                        size_b = size_a
                    seg_len_a = size_a / seg_a
                    seg_len_b = size_b / seg_b

                    for i in range(seg_a):
                        shape_bm.verts.new(Vector((size_b / 2 * -1, seg_len_a * i - (size_a / 2), 0)))
                    for i in range(seg_b):
                        shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2, 0)))
                    for i in range(seg_a, 0, -1):
                        shape_bm.verts.new(Vector((size_b / 2, seg_len_a * i - (size_a / 2), 0)))
                    for i in range(seg_b, 0, -1):
                        shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2 * -1, 0)))

                    shape_verts = shape_bm.verts[:]
                    for i in range(len(shape_verts)):
                        shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)]))
                    shape_edges = shape_bm.edges[:]

                elif self.shape == "PATTERN":
                    pattern_idx = context.scene.perfect_shape.active_pattern
                    pattern = context.scene.perfect_shape.patterns[int(pattern_idx)]
                    if len(pattern.verts) == 0:
                        self.report({'WARNING'}, "Empty Pattern Data.")
                        del shape_bm
                        return {'FINISHED'}
                    if len(pattern.verts) != len(loop_verts):
                        self.report({'WARNING'}, "Pattern and loop vertices count must be the same.")
                        del shape_bm
                        return {'FINISHED'}
                    for pattern_vert in pattern.verts:
                        shape_bm.verts.new(Vector(pattern_vert.co))
                    shape_verts = shape_bm.verts[:]
                    for i in range(len(shape_verts)):
                        shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)]))
                    shape_edges = shape_bm.edges[:]

                elif self.shape == "OBJECT":
                    if self.target in bpy.data.objects:
                        shape_object = bpy.data.objects[self.target]
                        shape_bm.from_object(shape_object, context.scene)
                        loops = get_loops(shape_bm.edges[:])
                        if not loops or len(loops) > 1:
                            self.report({'WARNING'}, "Wrong mesh data.")
                            del shape_bm
                            return {'FINISHED'}
                        if len(loops[0][0][0]) > len(loop_verts):
                            self.report({'WARNING'}, "Shape and loop vertices count must be the same.")
                            del shape_bm
                            return {'FINISHED'}

                        shape_verts = loops[0][0][0]
                        shape_edges = loops[0][0][1]
                if shape_verts:
                    set_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx), [v.co.copy() for v in shape_verts])

            if shape_verts:
                context.scene.perfect_shape.preview_verts_count = loop_verts_len + self.span

                try:
                    center = get_cache(self.as_pointer(), "P_{}_{}".format(self.pivot_point, loop_idx))

                except CacheException:
                    if self.pivot_point == "CURSOR":
                        center = object.matrix_world.copy() * context.scene.cursor_location.copy()
                    else:
                        temp_bm = bmesh.new()
                        for loop_vert in loop_verts:
                            temp_bm.verts.new(loop_vert.co.copy())
                        temp_verts = temp_bm.verts[:]
                        for i in range(len(temp_verts)):
                            temp_bm.edges.new((temp_verts[i], temp_verts[(i + 1) % len(temp_verts)]))
                        temp_bm.faces.new(temp_bm.verts)
                        temp_bm.faces.ensure_lookup_table()
                        if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER':
                            center = temp_bm.faces[0].calc_center_bounds()
                        else:
                            center = temp_bm.faces[0].calc_center_median()
                        del temp_bm
                    set_cache(self.as_pointer(), "P_{}_{}".format(self.pivot_point, loop_idx), center)

                if self.projection == "NORMAL":
                    forward = calculate_normal([v.co.copy() for v in loop_verts])
                    normal_forward = reduce(
                        lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert)
                        else v1.copy() + v2.normal.copy(), loop_verts).normalized()
                    if forward.angle(normal_forward) - math.pi / 2 >= 1e-6:
                        forward.negate()
                else:
                    forward = Vector([v == self.projection for v in ["X", "Y", "Z"]])

                if self.invert_projection:
                    forward.negate()


                rotation_m = 1
                if context.space_data.pivot_point != "INDIVIDUAL_ORIGINS":

                    if (center+selection_center).dot(forward) > 0:
                        rotation_m = -1
                    # if center.cross(forward).angle(selection_center) >= math.pi / 2:
                    #     forward.negate()

                    # if cross.dot(center) < 0:
                    #     forward.negate()
                    # if(center + selection_center).dot(forward) < 0:
                    #     forward.negate()
                    # matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4()
                    # if (matrix_rotation * center).dot(matrix_rotation * (center + selection_center)) > 0:
                    #     forward.negate()
                    #     if loop_faces:
                    #         rotation_m = - 1
                if not is_clockwise(forward, center, loop_verts):
                    loop_verts.reverse()
                    loop_edges.reverse()

                matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4()
                matrix_translation = Matrix.Translation(center)

                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1)) * (1 + self.offset), verts=shape_verts)

                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation * matrix_rotation)

                loop_verts_co_2d = [(v.co * matrix_rotation).to_2d() for v in loop_verts]
                shape_verts_co_2d = [(v.co * matrix_rotation).to_2d() for v in shape_verts]

                loop_angle = box_fit_2d(loop_verts_co_2d)
                shape_angle = box_fit_2d(shape_verts_co_2d)

                correct_angle = 0
                if self.loop_rotation:
                    correct_angle = loop_angle

                if self.shape_rotation:
                    correct_angle += shape_angle

                if correct_angle != 0:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-correct_angle, 3, forward))

                kd_tree = mathutils.kdtree.KDTree(len(loop_verts))
                for idx, loop_vert in enumerate(loop_verts):
                    kd_tree.insert(loop_vert.co, idx)
                kd_tree.balance()
                shape_first_idx = kd_tree.find(shape_verts[0].co)[1]
                shift = shape_first_idx + self.shift
                if shift != 0:
                    loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)]

                if self.rotation != 0:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-self.rotation*rotation_m, 3, forward))

                bmesh.ops.translate(shape_bm, vec=self.shape_translation, verts=shape_bm.verts)
                center = Matrix.Translation(self.shape_translation) * center

                if not is_loop_boundary and self.use_ray_cast:
                    for shape_vert in shape_verts:
                        co = shape_vert.co
                        ray_cast_data = object_bvh.ray_cast(co, forward)
                        if ray_cast_data[0] is None:
                            ray_cast_data = object_bvh.ray_cast(co, -forward)
                        if ray_cast_data[0] is not None:
                            shape_vert.co = ray_cast_data[0]

                for idx, vert in enumerate(loop_verts):
                    vert.co = vert.co.lerp(shape_verts[idx].co, self.factor / 100)

                if not is_loop_boundary and is_loop_cyclic and loop_faces:
                    if self.fill_type != "ORIGINAL":
                        smooth = loop_faces[0].smooth
                        bmesh.ops.delete(object_bm, geom=loop_faces, context=5)

                        loop_faces = []
                        center_vert = object_bm.verts.new(center)
                        if self.use_ray_cast:
                            ray_cast_data = object_bvh.ray_cast(center_vert.co, forward)
                            if ray_cast_data[0] is None:
                                ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward)
                            if ray_cast_data[0] is not None:
                                center_vert.co = ray_cast_data[0]
                        for idx, vert in enumerate(loop_verts):
                            new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx + 1) % loop_verts_len]))
                            new_face.smooth = smooth
                            loop_faces.append(new_face)
                        bmesh.ops.recalc_face_normals(object_bm, faces=loop_faces)


                    if self.outset > 0.0:
                        outset_region_faces = bmesh.ops.inset_region(object_bm, faces=loop_faces,
                                                                     thickness=self.outset, use_even_offset=True,
                                                                     use_interpolate=True, use_outset=True)

                    if self.extrude == 0:
                        verts = loop_verts[:]
                        for face in loop_faces:
                            for vert in face.verts:
                                if vert not in verts:
                                    verts.append(vert)
                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(),
                                             verts=loop_verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                        if self.inset > 0.0:
                            bmesh.ops.inset_region(object_bm, faces=loop_faces,
                                                   thickness=self.inset,
                                                   use_even_offset=True,
                                                   use_interpolate=True)
                        if self.fill_type == "HOLE":
                            bmesh.ops.delete(object_bm, geom=loop_faces, context=5)
                        elif self.fill_type == "NGON":
                            bmesh.utils.face_join(loop_faces)

                    else:
                        verts = []
                        edges = []
                        faces = []
                        side_faces = []
                        side_edges = []
                        extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=loop_faces, use_keep_orig=True)
                        bmesh.ops.delete(object_bm, geom=loop_faces, context=5)
                        for geom in extrude_geom["geom"]:
                            if isinstance(geom, bmesh.types.BMVert):
                                verts.append(geom)
                            elif isinstance(geom, bmesh.types.BMFace):
                                faces.append(geom)
                            elif isinstance(geom, bmesh.types.BMEdge):
                                edges.append(geom)

                        for edge in loop_edges:
                            for face in edge.link_faces:
                                if any((e for e in face.edges if e in edges)):
                                    side_faces.append(face)
                                    for edge in face.edges:
                                        if edge not in side_edges and edge not in edges and edge not in loop_edges:
                                            side_edges.append(edge)

                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                        bmesh.ops.translate(object_bm,
                                            verts=verts,
                                            vec=forward * self.extrude)

                        cuts = max(self.cuts, self.cuts_rings)
                        if cuts > 0:
                            sub = bmesh.ops.subdivide_edges(object_bm, edges=side_edges, cuts=cuts)
                            loop_verts = []
                            first_verts = loop_edges[0].verts[:]
                            for edge in loop_edges:
                                if edge == loop_edges[0]:
                                    continue
                                if edge == loop_edges[1]:
                                    if first_verts[0] == edge.verts[0]:
                                        first_verts.reverse()
                                    loop_verts.extend(first_verts)
                                for vert in edge.verts:
                                    if vert not in loop_verts:
                                        loop_verts.append(vert)
                            split_edges = []
                            for geom in sub["geom_split"]:
                                if isinstance(geom, bmesh.types.BMEdge):
                                    split_edges.append(geom)

                            skip_edges = []
                            for vert in loop_verts:
                                for edge in vert.link_edges:
                                    if edge not in skip_edges and edge not in split_edges:
                                        skip_edges.append(edge)

                            start = self.cuts_shift % loop_verts_len
                            stop = self.cuts_shift % loop_verts_len
                            verts_list = loop_verts[start:] + loop_verts[:stop]
                            for i in range(self.cuts):
                                new_split_edges = []
                                for idx, vert in enumerate(verts_list):
                                    if idx < self.cuts_len+i or idx >= loop_verts_len-i:
                                        continue
                                    for edge in vert.link_edges:
                                        if edge in split_edges:
                                            other_vert = edge.other_vert(vert)
                                            bmesh.ops.weld_verts(object_bm, targetmap={other_vert: vert})
                                    for edge in vert.link_edges:
                                        if edge not in new_split_edges and edge not in skip_edges:
                                            new_split_edges.append(edge)

                                split_edges = new_split_edges

                            cut_edges = []
                            dissolve_edges = []
                            cut_skip = []
                            prev_edge = None
                            first_join = True
                            for i in range(self.cuts_rings):
                                if i >= self.cuts:
                                    break
                                split_edges = []
                                for idx, vert in enumerate(verts_list):
                                    if idx < self.cuts_len+i or idx >= loop_verts_len-i:
                                        for edge in vert.link_edges:
                                            if edge not in skip_edges and edge not in cut_skip:
                                                if prev_edge is not None and idx not in range(self.cuts_len):
                                                    if not any((v for v in edge.verts if v in prev_edge.verts)):
                                                        if edge not in dissolve_edges:
                                                            dissolve_edges.append(edge)

                                                if edge not in dissolve_edges and edge not in split_edges:
                                                    split_edges.append(edge)
                                                    cut_skip.append(edge)
                                                    #edge.select_set(True)
                                                prev_edge = edge

                                if first_join and len(cut_edges) == 1:
                                    cut_edges[0].extend(split_edges)
                                    first_join = False
                                else:
                                    cut_edges.append(split_edges)
                            # if dissolve_edges:
                            #     bmesh.ops.dissolve_edges(object_bm, edges=dissolve_edges)
                            # inner_verts = []
                            # for i, split_edges in enumerate(cut_edges):
                            #     for edge in split_edges:
                            #         sub = bmesh.ops.subdivide_edges(object_bm, edges=[edge], cuts=self.cuts-i)
                            #         sub_verts = [v for v in sub["geom_inner"] if isinstance(v, bmesh.types.BMVert)]
                            #         inner_verts.append(sub_verts)

                        if self.side_inset > 0.0:
                            inset_region = bmesh.ops.inset_region(object_bm, faces=side_faces,
                                                                  thickness=self.side_inset,
                                                                  use_even_offset=True, use_interpolate=True)

                        if self.inset > 0.0:
                            inset_region_faces = bmesh.ops.inset_region(object_bm, faces=faces, thickness=self.inset,
                                                                        use_even_offset=True, use_interpolate=True)
                        if self.fill_type == "HOLE":
                            bmesh.ops.delete(object_bm, geom=faces, context=5)
                        elif self.fill_type == "NGON":
                            bmesh.utils.face_join(faces)

            if not selected_faces and self.extrude != 0:
                self.report({'WARNING'}, "Please select faces to extrude.")

            shape_bm.clear()

        del object_bvh
        object_bm.normal_update()
        bmesh.update_edit_mesh(object.data)
        return {'FINISHED'}
    def execute(self, context):
        orbiter = context.scene.orbiter
        template_context = {
            'defname':
            os.path.splitext(os.path.basename(orbiter.header_file))[0].upper()
        }

        # First get the collision cage
        vert_list = []
        with bmesh_object(bpy.data.objects[orbiter.ccage]) as bm:
            for v in bm.verts:
                # Conversion to orbiter is: z=-y,y=z,x=-x ; tri[1]<-> tri[2] ; v=1-v

                # Convert to orbiter coordinate system
                x, y, z = convert_to_orbiter(v.co)

                # Clean up the numbers a little
                x = round(x, 2)
                y = round(y, 2)
                z = round(z, 2)

                s = '{{_V({x}, {y}, {z}), {props}}}'.format(
                    x=x, y=y, z=z, props='1e6, 1e5, 1.6')
                vert_list.append(s)

        template_context['ccage_verts'] = ',\n    '.join(vert_list)
        template_context['ccage_vert_count'] = len(vert_list)
        template_context['ccage_suffix'] = orbiter.ccage_suffix

        # Now get the rockets
        all_rockets = set()
        for group in orbiter.rocket_groups:
            all_rockets.update(bpy.data.groups[group.group].objects)

        rockets = []
        rockets_pos = []
        rockets_dir = []
        for rocket in all_rockets:
            if rocket.type == 'EMPTY':
                name = rocket.name.upper()

                mat = rocket.matrix_world * Matrix.Translation((0, 0, 1))
                dir = Vector(
                    (mat[0][3], mat[1][3], mat[2][3])) - rocket.location

                # The empties point in the direction the rocket fires, in orbiter it's the opposite.
                dir.negate()

                # index of this rocket
                index = len(rockets_pos)
                str_pos = '{{{}, {}, {}}}'.format(
                    *convert_to_orbiter(rocket.location))
                str_dir = '{{{}, {}, {}}}'.format(*convert_to_orbiter(dir))
                str_rocket = '#define {prefix}{name} {index};'.format(
                    prefix=orbiter.rocket_prefix, name=name, index=index)

                rockets_pos.append(str_pos)
                rockets_dir.append(str_dir)

                rockets.append(str_rocket)

        rocket_groups = ''
        for group in orbiter.rocket_groups:
            group_rockets = []
            for rocket in bpy.data.groups[group.group].objects:
                group_rockets.append(orbiter.rocket_prefix + rocket.name)

            rocket_groups += 'const int {group} {{\n    {rockets}\n}}\n'.format(
                group=orbiter.rocket_group_prefix + group.name,
                rockets=',\n    '.join(group_rockets))

        template_context['rocket_names'] = '\n'.join(rockets)

        template_context['rocket_pos_name'] = 'ROCKET_POSITIONS'
        template_context['rocket_pos'] = ',\n    '.join(rockets_pos)

        template_context['rocket_dir_name'] = 'ROCKET_DIRECTIONS'
        template_context['rocket_dir'] = ',\n    '.join(rockets_dir)
        template_context['rocket_groups'] = rocket_groups

        print(template_context)

        with open(bpy.path.abspath(orbiter.header_file), 'w') as f:
            f.write(_HEADER_FILE_TEMPLATE.format(**template_context))

        return {'FINISHED'}
Example #11
0
    def execute(self, context):

        if self.reset_values is True:
            self.reset_all_values()

        active_obj = context.scene.objects.active

        bm = bmesh.from_edit_mesh(active_obj.data)
        bm.verts.ensure_lookup_table()
        #verts = [v for v in bm.verts if v.select]

        # get loops
        loops = loop_t.get_connected_input(bm)
        loops = loop_t.check_loops(loops, bm)

        if not loops:
            self.report({'WARNING'}, "No Loops!")
            return {'CANCELLED'}

        first_indexes = []
        if isinstance(bm.select_history[0], bmesh.types.BMVert):
            for element in bm.select_history:
                first_indexes.append(element.index)
        elif isinstance(bm.select_history[0], bmesh.types.BMEdge):
            for element in bm.select_history:
                el_verts = element.verts
                first_indexes.append(el_verts[0].index)
                first_indexes.append(el_verts[1].index)

        for loop in loops:
            if loop[1] is True:
                continue

            loop_verts = []

            for ind in loop[0]:
                loop_verts.append(bm.verts[ind])

            #  for the case if we need to reverse it
            if loop[0][-1] in first_indexes:
                loop_verts = list(reversed(loop_verts))

            # reverse again for the direction
            if self.reverse_direction is True:
                loop_verts = list(reversed(loop_verts))

            # positions
            first_vert_pos = active_obj.matrix_world * loop_verts[0].co
            last_vert_pos = active_obj.matrix_world * loop_verts[-1].co

            loop_centr_orig = first_vert_pos.lerp(last_vert_pos, 0.5)
            relative_dist = (first_vert_pos - loop_centr_orig).length
            sidevec = (first_vert_pos - last_vert_pos).normalized()

            obj_matrix = active_obj.matrix_world
            obj_matrix_inv = obj_matrix.inverted()

            if self.direction_vector == 'Custom':
                rot_dir = Vector((self.rotate_axis[0], self.rotate_axis[1], self.rotate_axis[2])).normalized()
            elif self.direction_vector == 'MiddleCrossed':
                middle_nor = loop_verts[int(len(loop_verts) / 2)].normal.copy().normalized()
                middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix, obj_matrix_inv)
                rot_dir = middle_nor.cross(sidevec).normalized()

                # fix only for MiddleCrossed
                if not self.reverse_direction:
                    rot_dir.negate()

            else:
                middle_nor = loop_verts[int(len(loop_verts) / 2)].normal.copy().normalized()
                middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix, obj_matrix_inv)
                middle_nor = middle_nor.cross(sidevec).normalized()
                rot_dir = middle_nor.cross(sidevec).normalized()
                rot_dir.negate()

            upvec = rot_dir.cross(sidevec).normalized()
            loop_centr = ( self.upvec_offset * upvec * relative_dist ) + loop_centr_orig

            loop_angle = (first_vert_pos - loop_centr).normalized().angle((last_vert_pos - loop_centr).normalized())
            if self.upvec_offset > 0:
                loop_angle = math.radians( (360 - math.degrees(loop_angle)) )

            # even spread
            line_data = None
            if self.spread_mode == 'Even':
                world_verts = [active_obj.matrix_world * vert.co for vert in loop_verts]

                line_data = []
                line_length = 0.0
                for i, vec in enumerate(world_verts):
                    if i == 0:
                        line_data.append(0)
                    else:
                        line_length += (vec - world_verts[i-1]).length
                        line_data.append(line_length)

            # make arc!
            for i, vert in enumerate(loop_verts):
                if i != 0 and i != len(loop_verts)-1:
                    if self.spread_mode == 'Normal':
                        rot_angle = loop_angle * (i / (len(loop_verts) - 1))
                    else:
                        rot_angle = loop_angle * (line_data[i] / line_data[len(loop_verts)-1])

                    rot_mat = Matrix.Rotation(rot_angle, 3, rot_dir)
                    vert_pos = (rot_mat * (first_vert_pos - loop_centr)) + loop_centr

                    if self.scale_arc != 0:
                        vert_rel_dist = mathu.geometry.distance_point_to_plane(vert_pos, loop_centr_orig, upvec)
                        vert_rel_dist_max = self.upvec_offset + relative_dist

                        if vert_rel_dist != 0 and vert_rel_dist_max != 0:
                            vert_pos_offset = vert_rel_dist / vert_rel_dist_max
                            vert_pos += (self.scale_arc * upvec * vert_pos_offset * vert_rel_dist_max) 

                    # rotate arc
                    if self.rotate_arc_axis != 0:
                        rot_mat_2 = Matrix.Rotation(math.radians(self.rotate_arc_axis), 3, sidevec)
                        vert_pos = (rot_mat_2 * (vert_pos - loop_centr_orig)) + loop_centr_orig                            

                    vert.co = active_obj.matrix_world.inverted() * vert_pos

        bm.normal_update()
        bmesh.update_edit_mesh(active_obj.data)

        return {'FINISHED'}
Example #12
0
    def execute(self, context):

        if self.reset_values is True:
            self.reset_all_values()

        active_obj = context.scene.objects.active

        bm = bmesh.from_edit_mesh(active_obj.data)
        bm.verts.ensure_lookup_table()
        #verts = [v for v in bm.verts if v.select]

        # get loops
        loops = loop_t.get_connected_input(bm)
        loops = loop_t.check_loops(loops, bm)

        if not loops:
            self.report({'WARNING'}, "No Loops!")
            return {'CANCELLED'}

        first_indexes = []
        if isinstance(bm.select_history[0], bmesh.types.BMVert):
            for element in bm.select_history:
                first_indexes.append(element.index)
        elif isinstance(bm.select_history[0], bmesh.types.BMEdge):
            for element in bm.select_history:
                el_verts = element.verts
                first_indexes.append(el_verts[0].index)
                first_indexes.append(el_verts[1].index)

        for loop in loops:
            if loop[1] is True:
                continue

            loop_verts = []

            for ind in loop[0]:
                loop_verts.append(bm.verts[ind])

            #  for the case if we need to reverse it
            if loop[0][-1] in first_indexes:
                loop_verts = list(reversed(loop_verts))

            # reverse again for the direction
            if self.reverse_direction is True:
                loop_verts = list(reversed(loop_verts))

            # positions
            first_vert_pos = active_obj.matrix_world * loop_verts[0].co
            last_vert_pos = active_obj.matrix_world * loop_verts[-1].co

            loop_centr_orig = first_vert_pos.lerp(last_vert_pos, 0.5)
            relative_dist = (first_vert_pos - loop_centr_orig).length
            sidevec = (first_vert_pos - last_vert_pos).normalized()

            obj_matrix = active_obj.matrix_world
            obj_matrix_inv = obj_matrix.inverted()

            if self.direction_vector == 'Custom':
                rot_dir = Vector((self.rotate_axis[0], self.rotate_axis[1],
                                  self.rotate_axis[2])).normalized()
            elif self.direction_vector == 'MiddleCrossed':
                middle_nor = loop_verts[int(len(loop_verts) /
                                            2)].normal.copy().normalized()
                middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix,
                                                      obj_matrix_inv)
                rot_dir = middle_nor.cross(sidevec).normalized()

                # fix only for MiddleCrossed
                if not self.reverse_direction:
                    rot_dir.negate()

            else:
                middle_nor = loop_verts[int(len(loop_verts) /
                                            2)].normal.copy().normalized()
                middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix,
                                                      obj_matrix_inv)
                middle_nor = middle_nor.cross(sidevec).normalized()
                rot_dir = middle_nor.cross(sidevec).normalized()
                rot_dir.negate()

            upvec = rot_dir.cross(sidevec).normalized()
            loop_centr = (self.upvec_offset * upvec *
                          relative_dist) + loop_centr_orig

            loop_angle = (first_vert_pos - loop_centr).normalized().angle(
                (last_vert_pos - loop_centr).normalized())
            if self.upvec_offset > 0:
                loop_angle = math.radians((360 - math.degrees(loop_angle)))

            # even spread
            line_data = None
            if self.spread_mode == 'Even':
                world_verts = [
                    active_obj.matrix_world * vert.co for vert in loop_verts
                ]

                line_data = []
                line_length = 0.0
                for i, vec in enumerate(world_verts):
                    if i == 0:
                        line_data.append(0)
                    else:
                        line_length += (vec - world_verts[i - 1]).length
                        line_data.append(line_length)

            # make arc!
            for i, vert in enumerate(loop_verts):
                if i != 0 and i != len(loop_verts) - 1:
                    if self.spread_mode == 'Normal':
                        rot_angle = loop_angle * (i / (len(loop_verts) - 1))
                    else:
                        rot_angle = loop_angle * (
                            line_data[i] / line_data[len(loop_verts) - 1])

                    rot_mat = Matrix.Rotation(rot_angle, 3, rot_dir)
                    vert_pos = (rot_mat *
                                (first_vert_pos - loop_centr)) + loop_centr

                    if self.scale_arc != 0:
                        vert_rel_dist = mathu.geometry.distance_point_to_plane(
                            vert_pos, loop_centr_orig, upvec)
                        vert_rel_dist_max = self.upvec_offset + relative_dist

                        if vert_rel_dist != 0 and vert_rel_dist_max != 0:
                            vert_pos_offset = vert_rel_dist / vert_rel_dist_max
                            vert_pos += (self.scale_arc * upvec *
                                         vert_pos_offset * vert_rel_dist_max)

                    # rotate arc
                    if self.rotate_arc_axis != 0:
                        rot_mat_2 = Matrix.Rotation(
                            math.radians(self.rotate_arc_axis), 3, sidevec)
                        vert_pos = (
                            rot_mat_2 *
                            (vert_pos - loop_centr_orig)) + loop_centr_orig

                    vert.co = active_obj.matrix_world.inverted() * vert_pos

        bm.normal_update()
        bmesh.update_edit_mesh(active_obj.data)

        return {'FINISHED'}
Example #13
0
    def execute(self, context):
        object = context.object
        object.update_from_editmode()

        object_bm = bmesh.from_edit_mesh(object.data)
        object_bm.verts.ensure_lookup_table()
        object_bm.edges.ensure_lookup_table()
        object_bm.faces.ensure_lookup_table()

        selected_faces = [f for f in object_bm.faces if f.select]
        selected_edges = [e for e in object_bm.edges if e.select]
        selected_verts = [v for v in object_bm.verts if v.select]

        selection_center = Vector()

        if len(selected_edges) == 0:
            self.report({'WARNING'}, "Please select edges.")
            return {'CANCELLED'}

        try:
            cache_loops = get_cache(self.as_pointer(), "loops")
            loops = []
            for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in cache_loops:
                loops.append((([object_bm.verts[v] for v in loop_verts], [object_bm.edges[e] for e in loop_edges],
                               [object_bm.faces[f] for f in loop_faces]), is_loop_cyclic, is_loop_boundary))
        except CacheException:
            loops = get_loops(selected_edges, selected_faces)
            if loops:
                cache_loops = []
                for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in loops:
                    cache_loops.append((([v.index for v in loop_verts], [e.index for e in loop_edges],
                                         [f.index for f in loop_faces]), is_loop_cyclic, is_loop_boundary))
                set_cache(self.as_pointer(), "loops", cache_loops)

        if loops is None:
            self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).")
            return {'CANCELLED'}

        for vert in selected_verts:
            selection_center += vert.co
        selection_center /= len(selected_verts)

        object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False)

        refresh_icons()
        shape_bm = bmesh.new()
        for loop_idx, ((loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary) in enumerate(loops):
            if len(loop_edges) < 3:
                continue
            loop_verts_len = len(loop_verts)

            shape_verts = None
            shape_edges = None
            try:
                cache_verts = get_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx))
                tmp_vert = None
                for i, cache_vert in enumerate(cache_verts):
                    new_vert = shape_bm.verts.new(cache_vert)
                    if i > 0:
                        shape_bm.edges.new((tmp_vert, new_vert))
                    tmp_vert = new_vert
                shape_verts = shape_bm.verts[:]
                shape_bm.edges.new((shape_verts[-1], shape_verts[0]))
                shape_edges = shape_bm.edges[:]
            except CacheException:
                if self.shape == "CIRCLE":
                    a = sum([e.calc_length() for e in loop_edges]) / loop_verts_len
                    diameter = a / (2 * math.sin(math.pi / loop_verts_len))
                    shape_segments = loop_verts_len + self.span
                    shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, diameter=diameter)
                    shape_verts = shape_verts["verts"]
                    shape_edges = shape_bm.edges[:]
                elif self.shape == "RECTANGLE":
                    if loop_verts_len % 2 > 0:
                        self.report({'WARNING'}, "An odd number of edges.")
                        del shape_bm
                        return {'FINISHED'}
                    size = sum([e.calc_length() for e in loop_edges])

                    size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                    size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b
                    seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                    seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b)
                    if seg_a % 1 > 0:
                        self.report({'WARNING'}, "Incorrect sides ratio.")
                        seg_a += 1
                        seg_b += 2
                    seg_a = int(seg_a)
                    if self.is_square:
                        size_a = (size_a + size_b) / 2
                        size_b = size_a
                    seg_len_a = size_a / seg_a
                    seg_len_b = size_b / seg_b

                    for i in range(seg_a):
                        shape_bm.verts.new(Vector((size_b / 2 * -1, seg_len_a * i - (size_a / 2), 0)))
                    for i in range(seg_b):
                        shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2, 0)))
                    for i in range(seg_a, 0, -1):
                        shape_bm.verts.new(Vector((size_b / 2, seg_len_a * i - (size_a / 2), 0)))
                    for i in range(seg_b, 0, -1):
                        shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2 * -1, 0)))

                    shape_verts = shape_bm.verts[:]
                    for i in range(len(shape_verts)):
                        shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)]))
                    shape_edges = shape_bm.edges[:]
                elif self.shape == "PATTERN":
                    pattern_idx = context.scene.perfect_shape.active_pattern
                    pattern = context.scene.perfect_shape.patterns[int(pattern_idx)]
                    if len(pattern.verts) == 0:
                        self.report({'WARNING'}, "Empty Pattern Data.")
                        del shape_bm
                        return {'FINISHED'}
                    if len(pattern.verts) != len(loop_verts):
                        self.report({'WARNING'}, "Pattern and loop vertices count must be the same.")
                        del shape_bm
                        return {'FINISHED'}
                    for pattern_vert in pattern.verts:
                        shape_bm.verts.new(Vector(pattern_vert.co))
                    shape_verts = shape_bm.verts[:]
                    for i in range(len(shape_verts)):
                        shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)]))
                    shape_edges = shape_bm.edges[:]
                elif self.shape == "OBJECT":
                    if self.target in bpy.data.objects:
                        shape_object = bpy.data.objects[self.target]
                        shape_bm.from_object(shape_object, context.scene)
                        loops = get_loops(shape_bm.edges[:])
                        if not loops or len(loops) > 1:
                            self.report({'WARNING'}, "Wrong mesh data.")
                            del shape_bm
                            return {'FINISHED'}
                        if len(loops[0][0][0]) > len(loop_verts):
                            self.report({'WARNING'}, "Shape and loop vertices count must be the same.")
                            del shape_bm
                            return {'FINISHED'}

                        shape_verts = loops[0][0][0]
                        shape_edges = loops[0][0][1]
                if shape_verts:
                    set_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx), [v.co.copy() for v in shape_verts])

            if shape_verts is not None and len(shape_verts) > 0:
                if context.space_data.pivot_point == "CURSOR":
                    center = object.matrix_world.copy() * context.scene.cursor_location.copy()
                else:
                    temp_bm = bmesh.new()
                    for loop_vert in loop_verts:
                        temp_bm.verts.new(loop_vert.co.copy())
                    temp_verts = temp_bm.verts[:]
                    for i in range(len(temp_verts)):
                        temp_bm.edges.new((temp_verts[i], temp_verts[(i + 1) % len(temp_verts)]))
                    temp_bm.faces.new(temp_bm.verts)
                    temp_bm.faces.ensure_lookup_table()
                    if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER':
                        center = temp_bm.faces[0].calc_center_bounds()
                    else:
                        center = temp_bm.faces[0].calc_center_median()
                    del temp_bm

                context.scene.perfect_shape.preview_verts_count = loop_verts_len + self.span
                if self.projection == "NORMAL":
                    forward = calculate_normal([v.co.copy() for v in loop_verts])
                    normal_forward = reduce(
                        lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert)
                        else v1.copy() + v2.normal.copy(), loop_verts).normalized()

                    if normal_forward.angle(forward) - math.pi / 2 > 1e-6:
                        forward.negate()
                else:
                    forward = Vector([v == self.projection for v in ["X", "Y", "Z"]])

                if self.invert_projection:
                    forward.negate()

                matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4()
                matrix_translation = Matrix.Translation(center)

                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1)) * (1 + self.offset), verts=shape_verts)

                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation * matrix_rotation)

                if not is_clockwise(forward, center, loop_verts):
                    loop_verts.reverse()
                    loop_edges.reverse()

                if not is_clockwise(forward, center, shape_verts):
                    shape_verts.reverse()

                correct_angle_m = 1
                shift_m = 1

                if context.space_data.pivot_point != "INDIVIDUAL_ORIGINS":
                    if selection_center.dot(center.cross(forward)) >= 0:
                        # if    (center.cross(forward)).angle(selection_center) - math.pi/2 <= 1e-6:
                        correct_angle_m = -1
                        shift_m = -1

                loop_verts_co_2d, shape_verts_co_2d = None, None
                if loop_verts_co_2d is None:
                    loop_verts_co_2d = [(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts]
                    shape_verts_co_2d = [(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts]
                loop_angle = box_fit_2d(loop_verts_co_2d)
                shape_angle = box_fit_2d(shape_verts_co_2d)
                # if round(loop_angle, 4) == round(math.pi, 4):
                #     loop_angle = 0

                correct_angle = 0
                if self.loop_rotation:
                    correct_angle = loop_angle * correct_angle_m
                    if round(loop_angle, 3) == round(shape_angle, 3):
                        correct_angle -= shape_angle

                if self.shape_rotation:
                    correct_angle -= shape_angle

                if correct_angle != 0:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-correct_angle * correct_angle_m, 3, forward))

                kd_tree = mathutils.kdtree.KDTree(len(loop_verts))
                for idx, loop_vert in enumerate(loop_verts):
                    kd_tree.insert(loop_vert.co, idx)
                kd_tree.balance()
                shape_first_idx = kd_tree.find(shape_verts[0].co)[1]
                shift = shape_first_idx + self.shift * shift_m
                if shift != 0:
                    loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)]

                if self.rotation != 0:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-self.rotation * correct_angle_m, 3, forward))

                bmesh.ops.translate(shape_bm, vec=self.shape_translation, verts=shape_bm.verts)
                center = Matrix.Translation(self.shape_translation) * center

                if not is_loop_boundary and self.use_ray_cast:
                    for shape_vert in shape_verts:
                        co = shape_vert.co
                        ray_cast_data = object_bvh.ray_cast(co, forward)
                        if ray_cast_data[0] is None:
                            ray_cast_data = object_bvh.ray_cast(co, -forward)
                        if ray_cast_data[0] is not None:
                            shape_vert.co = ray_cast_data[0]

                for idx, vert in enumerate(loop_verts):
                    vert.co = vert.co.lerp(shape_verts[idx].co, self.factor / 100)

                if not is_loop_boundary and is_loop_cyclic and loop_faces:
                    if self.fill_type != "ORIGINAL":
                        smooth = loop_faces[0].smooth
                        bmesh.ops.delete(object_bm, geom=loop_faces, context=5)

                        loop_faces = []
                        center_vert = object_bm.verts.new(center)
                        if self.use_ray_cast:
                            ray_cast_data = object_bvh.ray_cast(center_vert.co, forward)
                            if ray_cast_data[0] is None:
                                ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward)
                            if ray_cast_data[0] is not None:
                                center_vert.co = ray_cast_data[0]
                        for idx, vert in enumerate(loop_verts):
                            new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx + 1) % loop_verts_len]))
                            new_face.smooth = smooth
                            loop_faces.append(new_face)
                        bmesh.ops.recalc_face_normals(object_bm, faces=loop_faces)

                    if self.outset > 0.0:
                        outset_region_faces = bmesh.ops.inset_region(object_bm, faces=loop_faces,
                                                                     thickness=self.outset, use_even_offset=True,
                                                                     use_interpolate=True, use_outset=True)

                    if self.extrude == 0:
                        verts = loop_verts[:]
                        for face in loop_faces:
                            for vert in face.verts:
                                if vert not in verts:
                                    verts.append(vert)
                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(),
                                             verts=loop_verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                        if self.inset > 0.0:
                            bmesh.ops.inset_region(object_bm, faces=loop_faces,
                                                   thickness=self.inset,
                                                   use_even_offset=True,
                                                   use_interpolate=True)
                        if self.fill_type == "HOLE":
                            bmesh.ops.delete(object_bm, geom=loop_faces, context=5)
                        elif self.fill_type == "NGON":
                            bmesh.utils.face_join(loop_faces)

                    else:
                        verts = []
                        edges = []
                        faces = []
                        side_faces = []
                        side_edges = []
                        extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=loop_faces, use_keep_orig=True)
                        bmesh.ops.delete(object_bm, geom=loop_faces, context=5)
                        for geom in extrude_geom["geom"]:
                            if isinstance(geom, bmesh.types.BMVert):
                                verts.append(geom)
                            elif isinstance(geom, bmesh.types.BMFace):
                                faces.append(geom)
                            elif isinstance(geom, bmesh.types.BMEdge):
                                edges.append(geom)

                        for edge in loop_edges:
                            for face in edge.link_faces:
                                if any((e for e in face.edges if e in edges)):
                                    side_faces.append(face)
                                    for edge in face.edges:
                                        if edge not in side_edges and edge not in edges and edge not in loop_edges:
                                            side_edges.append(edge)

                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                        bmesh.ops.translate(object_bm,
                                            verts=verts,
                                            vec=forward * self.extrude)

                        cuts = max(self.cuts, self.cuts_rings)
                        if cuts > 0:
                            sub = bmesh.ops.subdivide_edges(object_bm, edges=side_edges, cuts=cuts)
                            loop_verts = []
                            first_verts = loop_edges[0].verts[:]
                            for edge in loop_edges:
                                if edge == loop_edges[0]:
                                    continue
                                if edge == loop_edges[1]:
                                    if first_verts[0] == edge.verts[0]:
                                        first_verts.reverse()
                                    loop_verts.extend(first_verts)
                                for vert in edge.verts:
                                    if vert not in loop_verts:
                                        loop_verts.append(vert)
                            split_edges = []
                            for geom in sub["geom_split"]:
                                if isinstance(geom, bmesh.types.BMEdge):
                                    split_edges.append(geom)

                            skip_edges = []
                            for vert in loop_verts:
                                for edge in vert.link_edges:
                                    if edge not in skip_edges and edge not in split_edges:
                                        skip_edges.append(edge)

                            start = self.cuts_shift % loop_verts_len
                            stop = self.cuts_shift % loop_verts_len
                            verts_list = loop_verts[start:] + loop_verts[:stop]
                            for i in range(self.cuts):
                                new_split_edges = []
                                for idx, vert in enumerate(verts_list):
                                    if idx < self.cuts_len + i or idx >= loop_verts_len - i:
                                        continue
                                    for edge in vert.link_edges:
                                        if edge in split_edges:
                                            other_vert = edge.other_vert(vert)
                                            bmesh.ops.weld_verts(object_bm, targetmap={other_vert: vert})
                                    for edge in vert.link_edges:
                                        if edge not in new_split_edges and edge not in skip_edges:
                                            new_split_edges.append(edge)

                                split_edges = new_split_edges

                            cut_edges = []
                            dissolve_edges = []
                            cut_skip = []
                            prev_edge = None
                            first_join = True
                            for i in range(self.cuts_rings):
                                if i >= self.cuts:
                                    break
                                split_edges = []
                                for idx, vert in enumerate(verts_list):
                                    if idx < self.cuts_len + i or idx >= loop_verts_len - i:
                                        for edge in vert.link_edges:
                                            if edge not in skip_edges and edge not in cut_skip:
                                                if prev_edge is not None and idx not in range(self.cuts_len):
                                                    if not any((v for v in edge.verts if v in prev_edge.verts)):
                                                        if edge not in dissolve_edges:
                                                            dissolve_edges.append(edge)

                                                if edge not in dissolve_edges and edge not in split_edges:
                                                    split_edges.append(edge)
                                                    cut_skip.append(edge)
                                                    # edge.select_set(True)
                                                prev_edge = edge

                                if first_join and len(cut_edges) == 1:
                                    cut_edges[0].extend(split_edges)
                                    first_join = False
                                else:
                                    cut_edges.append(split_edges)
                            # if dissolve_edges:
                            #     bmesh.ops.dissolve_edges(object_bm, edges=dissolve_edges)
                            # inner_verts = []
                            # for i, split_edges in enumerate(cut_edges):
                            #     for edge in split_edges:
                            #         sub = bmesh.ops.subdivide_edges(object_bm, edges=[edge], cuts=self.cuts-i)
                            #         sub_verts = [v for v in sub["geom_inner"] if isinstance(v, bmesh.types.BMVert)]
                            #         inner_verts.append(sub_verts)

                        if self.side_inset > 0.0:
                            inset_region = bmesh.ops.inset_region(object_bm, faces=side_faces,
                                                                  thickness=self.side_inset,
                                                                  use_even_offset=True, use_interpolate=True)

                        if self.inset > 0.0:
                            inset_region_faces = bmesh.ops.inset_region(object_bm, faces=faces, thickness=self.inset,
                                                                        use_even_offset=True, use_interpolate=True)
                        if self.fill_type == "HOLE":
                            bmesh.ops.delete(object_bm, geom=faces, context=5)
                        elif self.fill_type == "NGON":
                            bmesh.utils.face_join(faces)

            shape_bm.clear()

        del object_bvh
        object_bm.normal_update()
        bmesh.update_edit_mesh(object.data)
        return {'FINISHED'}
Example #14
0
    def execute(self, context):
        object = context.object
        object.update_from_editmode()

        object_bm = bmesh.from_edit_mesh(object.data)

        selected_edges = [e for e in object_bm.edges if e.select]
        selected_verts = [v for v in object_bm.verts if v.select]
        selected_verts_fin = []

        if len(selected_edges) == 0:
            self.report({'WARNING'}, "Please select edges.")
            return {'CANCELLED'}

        loops = prepare_loops(selected_edges[:])

        if loops is None:
            self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).")
            return {'CANCELLED'}

        object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False)

        refresh_icons()

        for (loop_verts, loop_edges), is_loop_cyclic, is_loop_boundary in loops:
            if len(loop_edges) < 3:
                continue
            loop_verts_len = len(loop_verts)
            context.window_manager.perfect_shape.preview_verts_count = loop_verts_len
            if self.projection == "NORMAL":
                if is_loop_boundary:
                    forward = calculate_normal([v.co for v in loop_verts])
                else:
                    forward = reduce(
                        lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert)
                        else v1.copy() + v2.normal.copy(), loop_verts).normalized()
            else:
                forward = Vector([v == self.projection for v in ["X", "Y", "Z"]])

            if self.invert_projection:
                forward.negate()

            shape_bm = bmesh.new()

            if context.space_data.pivot_point == "CURSOR":
                center = object.matrix_world.copy() * context.scene.cursor_location.copy()
            else:
                for loop_vert in loop_verts:
                    shape_bm.verts.new(loop_vert.co.copy())
                shape_bm.faces.new(shape_bm.verts)
                shape_bm.faces.ensure_lookup_table()
                if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER':
                    center = shape_bm.faces[0].calc_center_bounds()
                else:
                    center = shape_bm.faces[0].calc_center_median()

                shape_bm.clear()

            matrix_rotation = forward.to_track_quat('Z', 'X').to_matrix().to_4x4()
            matrix_translation = Matrix.Translation(center)

            shape_bm = bmesh.new()
            shape_verts = None
            if self.shape == "CIRCLE":
                diameter = sum([e.calc_length() for e in loop_edges]) / (2*math.pi)
                diameter += self.offset
                shape_segments = loop_verts_len + self.span
                shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments,
                                                      diameter=diameter, matrix=matrix_translation*matrix_rotation)
                shape_verts = shape_verts["verts"]
            elif self.shape == "RECTANGLE":
                if loop_verts_len % 2 > 0:
                    self.report({'WARNING'}, "An odd number of edges.")
                    del shape_bm
                    return {'FINISHED'}
                size = sum([e.calc_length() for e in loop_edges])

                size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b
                seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b)
                if seg_a % 1 > 0:
                    self.report({'WARNING'}, "Incorrect sides ratio.")
                    seg_a += 1
                    seg_b += 2
                seg_a = int(seg_a)
                if self.is_square:
                    size_a = (size_a + size_b) / 2
                    size_b = size_a
                seg_len_a = size_a / seg_a
                seg_len_b = size_b / seg_b

                for i in range(seg_a):
                    shape_bm.verts.new(Vector((size_b/2*-1, seg_len_a*i-(size_a/2), 0)))
                for i in range(seg_b):
                    shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2, 0)))
                for i in range(seg_a, 0, -1):
                    shape_bm.verts.new(Vector((size_b/2, seg_len_a*i-(size_a/2), 0)))
                for i in range(seg_b, 0, -1):
                    shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2*-1, 0)))

                shape_verts = shape_bm.verts[:]
                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts)
                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation)
            elif self.shape == "PATTERN":
                if len(object.perfect_pattern.vertices) == 0:
                    self.report({'WARNING'}, "Empty Pattern Data.")
                    del shape_bm
                    return {'FINISHED'}
                if len(object.perfect_pattern.vertices) != len(loop_verts):
                    self.report({'WARNING'}, "Pattern and loop vertices count must be the same.")
                    del shape_bm
                    return {'FINISHED'}
                for pattern_vert in object.perfect_pattern.vertices:
                    shape_bm.verts.new(Vector(pattern_vert.co))
                shape_verts = shape_bm.verts[:]
                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts)
                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation)
            elif self.shape == "OBJECT":
                if self.target in bpy.data.objects:
                    shape_object = bpy.data.objects[self.target]
                    shape_bm.from_object(shape_object, context.scene)
                    loops = prepare_loops(shape_bm.edges[:])
                    if not loops or len(loops) > 1:
                        self.report({'WARNING'}, "Wrong mesh data.")
                        del shape_bm
                        return {'FINISHED'}
                    if len(loops[0][0][0]) != len(loop_verts):
                        self.report({'WARNING'}, "Shape and loop vertices count must be the same.")
                        del shape_bm
                        return {'FINISHED'}

                    shape_verts = shape_bm.verts[:]
                    bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts)
                    bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation)

            if shape_verts is not None and len(shape_verts) > 0:
                if not is_clockwise(forward, center, loop_verts):
                    loop_verts.reverse()
                    loop_edges.reverse()
                if not is_clockwise(forward, center, shape_verts):
                    shape_verts.reverse()

                loop_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts])
                shape_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts])

                if abs(abs(loop_angle) - abs(shape_angle)) <= 0.01:
                    loop_angle = 0
                correct_angle = loop_angle + self.rotation

                if self.shape_rotation:
                    correct_angle -= shape_angle

                if correct_angle != 0 and not self.active_as_first:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-correct_angle, 3, forward))

                active = object_bm.select_history.active
                if self.active_as_first and isinstance(active, bmesh.types.BMVert) and active in loop_verts:
                    shift = loop_verts.index(active)
                else:
                    kd_tree = mathutils.kdtree.KDTree(len(loop_verts))
                    for idx, loop_vert in enumerate(loop_verts):
                        kd_tree.insert(loop_vert.co, idx)
                    kd_tree.balance()
                    shape_first_idx = kd_tree.find(shape_verts[0].co)[1]
                    shift = shape_first_idx + self.shift
                if shift != 0:
                    loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)]

                if not is_loop_boundary and self.use_ray_cast:
                    for shape_vert in shape_verts:
                        co = shape_vert.co
                        ray_cast_data = object_bvh.ray_cast(co, forward)
                        if ray_cast_data[0] is None:
                            ray_cast_data = object_bvh.ray_cast(co, -forward)
                        if ray_cast_data[0] is not None:
                            shape_vert.co = ray_cast_data[0]

                for idx, vert in enumerate(loop_verts):
                    vert.co = vert.co.lerp(shape_verts[idx].co, self.factor/100)

                if not is_loop_boundary and is_loop_cyclic:
                    object_bm.select_flush_mode()
                    select_only(object_bm, loop_edges, {"EDGE"})
                    bpy.ops.mesh.loop_to_region()  # Ugly.
                    inset_faces = [f for f in object_bm.faces[:] if f.select]

                    if self.fill_type != "ORIGINAL":
                        smooth = inset_faces[0].smooth
                        bmesh.ops.delete(object_bm, geom=inset_faces, context=5)

                        inset_faces = []
                        center_vert = object_bm.verts.new(center)
                        if self.use_ray_cast:
                            ray_cast_data = object_bvh.ray_cast(center_vert.co, forward)
                            if ray_cast_data[0] is None:
                                ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward)
                            if ray_cast_data[0] is not None:
                                center_vert.co = ray_cast_data[0]
                        for idx, vert in enumerate(loop_verts):
                            new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx+1) % loop_verts_len]))
                            new_face.smooth = smooth
                            inset_faces.append(new_face)
                        bmesh.ops.recalc_face_normals(object_bm, faces=inset_faces)

                    selected_co = []
                    for vert in selected_verts:
                        if vert.is_valid:
                            selected_co.append(vert.co.copy())

                    outset_region_faces = []
                    if self.outset > 0.0:
                        outset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces,
                                                                     thickness=self.outset, use_even_offset=True,
                                                                     use_interpolate=True, use_outset=True)
                        outset_region_faces = outset_region_faces["faces"]

                    inset_region_faces = []
                    if self.inset > 0.0:
                        inset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces, thickness=self.inset,
                                                                    use_even_offset=True, use_interpolate=True)
                        inset_region_faces = inset_region_faces["faces"]

                    new_selected_verts = []

                    for face in set(inset_region_faces+inset_faces):
                        for vert in face.verts:
                            if vert.co in selected_co:
                                new_selected_verts.append(vert)
                                selected_co.remove(vert.co)

                    selected_verts_fin.append(new_selected_verts)
                    select_only(object_bm, new_selected_verts, {"EDGE"})

                    if self.fill_type == "HOLE":
                        bmesh.ops.delete(object_bm, geom=inset_faces, context=5)
                        inset_faces = []
                    elif self.fill_type == "NGON":
                        inset_faces = [bmesh.utils.face_join(inset_faces)]

                    if self.fill_flatten and self.extrude == 0:
                        if len(inset_region_faces + inset_faces) > 0:
                            verts = list(set(reduce(lambda v1, v2: list(v1) + list(v2),
                                                    [v.verts for v in inset_region_faces + inset_faces])))
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                    bmesh.ops.recalc_face_normals(object_bm, faces=outset_region_faces+inset_region_faces+inset_faces)
                    if self.extrude != 0:
                        extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=inset_region_faces+inset_faces)
                        verts = [v for v in extrude_geom['geom'] if isinstance(v, bmesh.types.BMVert)]

                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)
                        bmesh.ops.delete(object_bm, geom=inset_region_faces+inset_faces, context=5)
                        bmesh.ops.translate(object_bm,
                                            verts=verts,
                                            vec=forward * self.extrude)
            del shape_bm
        if selected_verts_fin:
            select_only(object_bm, reduce(lambda x, y: x + y, selected_verts_fin), {"EDGE"})
        object_bm.select_flush(True)
        del object_bvh
        bmesh.update_edit_mesh(object.data)
        return {'FINISHED'}
    def execute(self, context):
        og_cursor_setting = str(context.scene.cursor.rotation_mode)
        self.cursorOP = bpy.context.scene.kekit.cursorfit

        if not self.cursorOP:
            # GRAB CURRENT ORIENT & PIVOT (to restore at the end)
            og_orientation = str(bpy.context.scene.transform_orientation_slots[0].type)
            og_pivot = str(bpy.context.scene.tool_settings.transform_pivot_point)

        if bpy.context.mode == "EDIT_MESH":
            sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
            obj = bpy.context.edit_object
            obj_mtx = obj.matrix_world.copy()
            bm = bmesh.from_edit_mesh(obj.data)

            bm.verts.ensure_lookup_table()
            bm.edges.ensure_lookup_table()
            bm.faces.ensure_lookup_table()

            vert_mode = True
            sel_verts = [v for v in bm.verts if v.select]
            sel_count = len(sel_verts)

            if sel_count == 0:
                bpy.ops.view3d.snap_cursor_to_center()
                return {'FINISHED'}

            # POLY MODE -----------------------------------------------------------------------
            if sel_mode[2]:
                sel_poly = [p for p in bm.faces if p.select]

                if sel_poly:

                    # sel_islands = get_islands(bm, sel_verts)

                    v_normals = [p.normal for p in sel_poly]
                    v_tan = [p.calc_tangent_edge_pair() for p in sel_poly]
                    face = sel_poly[-1]

                    normal = correct_normal(obj_mtx, sum(v_normals, Vector()) / len(v_normals))
                    tangent = correct_normal(obj_mtx, sum(v_tan, Vector()) / len(v_tan))

                    if len(sel_poly) == 1:
                        pos = obj_mtx @ bm.faces[face.index].calc_center_median()
                    else:
                        ps = [v.co for v in sel_verts]
                        pos = obj_mtx @ average_vector(ps)

                    # fallback for all faces selected type of scenarios
                    if sum(normal) == 0:
                        normal = Vector((0,0,1))
                        tangent = Vector((1,0,0))

                    rot_mtx = rotation_from_vector(normal, tangent, rw=False)
                    set_cursor(rot_mtx, pos=pos)

                else:
                    bpy.ops.view3d.snap_cursor_to_center()

                vert_mode = False


            # EDGE MODE -----------------------------------------------------------------------
            if sel_mode[1]:

                loop_mode = False
                line_mode = False

                sel_edges = [e for e in bm.edges if e.select]
                e_count = len(sel_edges)

                if e_count > 1:
                    vps = [e.verts[:] for e in sel_edges]
                    loops = get_loops(vps, legacy=True)
                    if len(loops) >= 1 and loops[0][0] != loops[0][-1]:
                        fl = list(set(flatten(vps)))
                        if len(fl) == len(loops[0]):
                            line_mode = True

                if e_count == 1:

                    ev = sel_edges[0].verts[:]
                    n = Vector((ev[0].normal + ev[1].normal) * .5).normalized()
                    t_v = Vector((ev[0].co - ev[1].co).normalized())
                    vert_mode = False

                elif e_count == 2 or line_mode:
                    shared_face = []
                    for f in sel_edges[0].link_faces:
                        for fe in sel_edges[1].link_faces:
                            if fe == f:
                                shared_face = f
                                break

                    ev = sel_edges[0].verts[:]
                    etv = sel_edges[1].verts[:]

                    n = Vector((ev[0].normal + ev[1].normal)*.5).normalized()
                    t_v = Vector((etv[0].normal + etv[1].normal)*.5).normalized()

                    if abs(round(n.dot(t_v),2)) == 1 or shared_face or line_mode:
                        avg_v, avg_e = [], []
                        for e in sel_edges:
                            avg_v.append((e.verts[0].normal + e.verts[1].normal) * .5)
                            uv = Vector(e.verts[0].co - e.verts[1].co).normalized()
                            avg_e.append(uv)

                        n = average_vector(avg_v)
                        # I forgot why i needed these checks?
                        if sum(n) == 0:
                            n = avg_v[0]
                        t_v = average_vector(avg_e)
                        if sum(t_v) == 0:
                            t_v = avg_e[0]

                        if shared_face:
                            t_v = avg_e[0]

                        vert_mode = False


                elif e_count > 2:
                    loop_mode = True

                    startv = Vector(sel_edges[0].verts[0].co - sel_edges[0].verts[1].co).normalized()
                    cv1 = Vector(sel_edges[1].verts[0].co - sel_edges[1].verts[1].co).normalized()
                    cdot = abs(round(startv.dot(cv1), 6))

                    for e in sel_edges[2:]:
                        v = Vector(e.verts[0].co - e.verts[1].co).normalized()
                        vdot = abs(round(startv.dot(v), 3))
                        if vdot < cdot:
                            cv1 = v
                            cdot = vdot

                    n = startv
                    t_v = cv1
                    n.negate()
                    vert_mode = False

                # final pass
                n = correct_normal(obj_mtx, n)
                t_v = correct_normal(obj_mtx, t_v)
                n = n.cross(t_v)

                # vert average fallback
                if sum(t_v) == 0 or sum(n) == 0:
                    vert_mode = True

                if not vert_mode:
                    if loop_mode:
                        rot_mtx = rotation_from_vector(n, t_v, rotate90=True)
                    else:
                        rot_mtx = rotation_from_vector(n, t_v, rotate90=False)

                    set_cursor(rot_mtx)


            # VERT (& GENERAL AVERAGE) MODE -----------------------------------------------------------------------
            if sel_mode[0] or vert_mode:

                if sel_count == 2:
                    n = Vector(sel_verts[0].co - sel_verts[1].co).normalized()
                    v_n = [v.normal for v in sel_verts]
                    t_v = correct_normal(obj_mtx, sum(v_n, Vector()) / len(v_n))
                    n = correct_normal(obj_mtx, n)
                    t_v = t_v.cross(n)

                    rot_mtx = rotation_from_vector(n, t_v)
                    set_cursor(rot_mtx)

                elif sel_count == 3:
                    cv = [v.co for v in sel_verts]

                    # make triangle vectors, sort to avoid the hypot.vector
                    h = tri_points_order(cv)
                    tri = sel_verts[h[0]].co, sel_verts[h[1]].co, sel_verts[h[2]].co
                    v1 = Vector((tri[0] - tri[1])).normalized()
                    v2 = Vector((tri[0] - tri[2])).normalized()
                    v1 = correct_normal(obj_mtx, v1)
                    v2 = correct_normal(obj_mtx, v2)
                    n_v = v1.cross(v2)

                    # flipcheck
                    v_n = [v.normal for v in sel_verts]
                    ncheck = correct_normal(obj_mtx, sum(v_n, Vector()) / len(v_n))
                    if ncheck.dot(n_v) < 0:
                        n_v.negate()

                    # tangentcheck
                    c1 = n_v.cross(v1).normalized()
                    c2 = n_v.cross(v2).normalized()
                    if c1.dot(n_v) > c2.dot(n_v):
                        u_v = c2
                    else:
                        u_v = c1
                    t_v = u_v.cross(n_v).normalized()

                    rot_mtx = rotation_from_vector(n_v, t_v)
                    set_cursor(rot_mtx)

                elif sel_count != 0:

                    v_n = [v.normal for v in sel_verts]
                    n = correct_normal(obj_mtx, sum(v_n, Vector()) / len(v_n))

                    if sel_count >= 1:
                        if sel_count == 1:
                            if not sel_verts[0].link_edges:
                                # floater vert check -> world rot
                                t_c = Vector((1,0,0))
                                n = Vector((0,0,1))
                            else:
                                t_c = sel_verts[0].co - sel_verts[0].link_edges[0].other_vert(sel_verts[0]).co
                        else:
                            t_c = sel_verts[0].co - sel_verts[1].co

                        t_c = correct_normal(obj_mtx, t_c)
                        t_v = n.cross(t_c).normalized()

                        rot_mtx = rotation_from_vector(n, t_v)
                        set_cursor(rot_mtx)

                elif sel_count == 0:
                    bpy.ops.view3d.snap_cursor_to_center()

                bm.select_flush_mode()
                bmesh.update_edit_mesh(obj.data)

        # OBJECT MODE -----------------------------------------------------------------------
        elif bpy.context.mode == "OBJECT":
            sel_obj = [o for o in context.selected_objects]
            hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast(context, self.mouse_pos)
            if hit_normal and hit_obj:
                obj_mtx = hit_obj.matrix_world.copy()

                bm = bmesh.new()
                bm.from_mesh(hit_obj.data)
                bm.faces.ensure_lookup_table()

                normal = bm.faces[hit_face].normal
                tangent = bm.faces[hit_face].calc_tangent_edge()
                pos = obj_mtx @ bm.faces[hit_face].calc_center_median()

                rot_mtx = rotation_from_vector(normal, tangent, rw=False)
                rot_mtx = obj_mtx @ rot_mtx
                set_cursor(rot_mtx, pos=pos)

            elif len(sel_obj) == 1 and hit_obj not in sel_obj:
                context.scene.cursor.location = sel_obj[0].location
                context.scene.cursor.rotation_euler = sel_obj[0].rotation_euler
                if self.cursorOP:
                    bpy.ops.transform.select_orientation(orientation="CURSOR")
                    bpy.context.tool_settings.transform_pivot_point = "CURSOR"

            elif len(sel_obj) > 1 and hit_obj not in sel_obj:
                v = Vector(sel_obj[0].location - sel_obj[-1].location).normalized()
                if round(abs(v.dot(Vector((1,0,0)) )),3) == 1:
                    u = Vector((0,0,1))
                else:
                    u = Vector((-1,0,0))
                t = v.cross(u).normalized()
                rot_mtx = rotation_from_vector(v, t, rw=False)
                context.scene.cursor.rotation_euler = rot_mtx.to_euler()
                context.scene.cursor.location = average_vector([o.location for o in sel_obj])

            else:
                bpy.ops.view3d.snap_cursor_to_center()

        if not self.cursorOP:
            # RESET OP TRANSFORMS
            bpy.ops.transform.select_orientation(orientation=og_orientation)
            bpy.context.scene.tool_settings.transform_pivot_point = og_pivot

        if og_cursor_setting != "QUATERNION":
            # just gonna go ahead and assume no one uses this as default, for back-compatibility reasons...
            context.scene.cursor.rotation_mode = og_cursor_setting
        else:
            context.scene.cursor.rotation_mode = 'XYZ'

        return {'FINISHED'}
Example #16
0
    def execute(self, context):
        object = context.object
        object.update_from_editmode()

        object_bm = bmesh.from_edit_mesh(object.data)

        selected_edges = [e for e in object_bm.edges if e.select]
        selected_verts = [v for v in object_bm.verts if v.select]
        selected_verts_fin = []

        if len(selected_edges) == 0:
            self.report({'WARNING'}, "Please select edges.")
            return {'CANCELLED'}

        loops = prepare_loops(selected_edges[:])

        if loops is None:
            self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).")
            return {'CANCELLED'}

        object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False)

        for (loop_verts, loop_edges), is_loop_cyclic, is_loop_boundary in loops:
            if len(loop_edges) < 3:
                continue
            loop_verts_len = len(loop_verts)
            if self.projection == "NORMAL":
                if is_loop_boundary:
                    forward = calculate_normal([v.co for v in loop_verts])
                else:
                    forward = reduce(
                        lambda v1, v2: v1.normal.copy() if isinstance(v1, bmesh.types.BMVert)
                        else v1.copy() + v2.normal.copy(), loop_verts).normalized()
            else:
                forward = Vector([v == self.projection for v in ["X", "Y", "Z"]])

            if self.invert_projection:
                forward.negate()

            shape_bm = bmesh.new()

            if context.space_data.pivot_point == "CURSOR":
                center = object.matrix_world.copy() * context.scene.cursor_location.copy()
            else:
                for loop_vert in loop_verts:
                    shape_bm.verts.new(loop_vert.co.copy())
                shape_bm.faces.new(shape_bm.verts)
                shape_bm.faces.ensure_lookup_table()
                if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER':
                    center = shape_bm.faces[0].calc_center_bounds()
                else:
                    center = shape_bm.faces[0].calc_center_median()

                shape_bm.clear()

            matrix_rotation = forward.to_track_quat('Z', 'X').to_matrix().to_4x4()
            matrix_translation = Matrix.Translation(center)

            shape_bm = bmesh.new()
            shape_verts = None
            if self.shape == "CIRCLE":
                diameter = sum([e.calc_length() for e in loop_edges]) / (2*math.pi)
                diameter += self.offset
                shape_segments = loop_verts_len + self.span
                shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments,
                                                      diameter=diameter, matrix=matrix_translation*matrix_rotation)
                shape_verts = shape_verts["verts"]
            elif self.shape == "RECTANGLE":
                if loop_verts_len % 2 > 0:
                    self.report({'WARNING'}, "An odd number of edges.")
                    del shape_bm
                    return {'FINISHED'}
                size = sum([e.calc_length() for e in loop_edges])

                size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b
                seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a
                seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b)
                if seg_a % 1 > 0:
                    self.report({'WARNING'}, "Incorrect sides ratio.")
                    seg_a += 1
                    seg_b += 2
                seg_a = int(seg_a)
                if self.is_square:
                    size_a = (size_a + size_b) / 2
                    size_b = size_a
                seg_len_a = size_a / seg_a
                seg_len_b = size_b / seg_b

                for i in range(seg_a):
                    shape_bm.verts.new(Vector((size_b/2*-1, seg_len_a*i-(size_a/2), 0)))
                for i in range(seg_b):
                    shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2, 0)))
                for i in range(seg_a, 0, -1):
                    shape_bm.verts.new(Vector((size_b/2, seg_len_a*i-(size_a/2), 0)))
                for i in range(seg_b, 0, -1):
                    shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2*-1, 0)))

                shape_verts = shape_bm.verts[:]
                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts)
                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation)
            elif self.shape == "PATTERN":
                if len(object.perfect_pattern) == 0:
                    self.report({'WARNING'}, "Empty Pattern Data.")
                    del shape_bm
                    return {'FINISHED'}
                if len(object.perfect_pattern) != len(loop_verts):
                    self.report({'WARNING'}, "Pattern and loop vertices count must be the same.")
                    del shape_bm
                    return {'FINISHED'}
                for pattern_vert in object.perfect_pattern:
                    shape_bm.verts.new(Vector(pattern_vert.co))
                shape_verts = shape_bm.verts[:]
                bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts)
                bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation)
            elif self.shape == "OBJECT":
                if self.target in bpy.data.objects:
                    shape_object = bpy.data.objects[self.target]
                    shape_bm.from_object(shape_object, context.scene)
                    loops = prepare_loops(shape_bm.edges[:])
                    if loops is None or len(loops) > 1:
                        self.report({'WARNING'}, "Wrong mesh data.")
                        del shape_bm
                        return {'FINISHED'}

                    shape_verts = shape_bm.verts[:]
                    bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts)
                    bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation)

            if shape_verts is not None and len(shape_verts) > 0:
                if not is_clockwise(forward, center, loop_verts):
                    loop_verts.reverse()
                    loop_edges.reverse()
                if not is_clockwise(forward, center, shape_verts):
                    shape_verts.reverse()

                loop_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts])
                shape_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts])

                if abs(abs(loop_angle) - abs(shape_angle)) <= 0.01:
                    loop_angle = 0
                correct_angle = loop_angle + self.rotation

                if self.shape_rotation:
                    correct_angle -= shape_angle

                if correct_angle != 0 and not self.active_as_first:
                    bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center,
                                     matrix=Matrix.Rotation(-correct_angle, 3, forward))

                active = object_bm.select_history.active
                if self.active_as_first and isinstance(active, bmesh.types.BMVert) and active in loop_verts:
                    shift = loop_verts.index(active)
                else:
                    kd_tree = mathutils.kdtree.KDTree(len(loop_verts))
                    for idx, loop_vert in enumerate(loop_verts):
                        kd_tree.insert(loop_vert.co, idx)
                    kd_tree.balance()
                    shape_first_idx = kd_tree.find(shape_verts[0].co)[1]
                    shift = shape_first_idx + self.shift
                if shift != 0:
                    loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)]

                if not is_loop_boundary and self.use_ray_cast:
                    for shape_vert in shape_verts:
                        co = shape_vert.co
                        ray_cast_data = object_bvh.ray_cast(co, forward)
                        if ray_cast_data[0] is None:
                            ray_cast_data = object_bvh.ray_cast(co, -forward)
                        if ray_cast_data[0] is not None:
                            shape_vert.co = ray_cast_data[0]

                for idx, vert in enumerate(loop_verts):
                    vert.co = vert.co.lerp(shape_verts[idx].co, self.factor)

                if not is_loop_boundary and is_loop_cyclic:
                    object_bm.select_flush_mode()
                    select_only(object_bm, loop_edges, {"EDGE"})
                    bpy.ops.mesh.loop_to_region()  # Ugly.
                    inset_faces = [f for f in object_bm.faces[:] if f.select]

                    if self.fill_type != "ORIGINAL":
                        smooth = inset_faces[0].smooth
                        bmesh.ops.delete(object_bm, geom=inset_faces, context=5)

                        inset_faces = []
                        center_vert = object_bm.verts.new(center)
                        if self.use_ray_cast:
                            ray_cast_data = object_bvh.ray_cast(center_vert.co, forward)
                            if ray_cast_data[0] is None:
                                ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward)
                            if ray_cast_data[0] is not None:
                                center_vert.co = ray_cast_data[0]
                        for idx, vert in enumerate(loop_verts):
                            new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx+1) % loop_verts_len]))
                            new_face.smooth = smooth
                            inset_faces.append(new_face)
                        bmesh.ops.recalc_face_normals(object_bm, faces=inset_faces)

                    selected_co = []
                    for vert in selected_verts:
                        if vert.is_valid:
                            selected_co.append(vert.co.copy())

                    outset_region_faces = []
                    if self.outset > 0.0:
                        outset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces,
                                                                     thickness=self.outset, use_even_offset=True,
                                                                     use_interpolate=True, use_outset=True)
                        outset_region_faces = outset_region_faces["faces"]

                    inset_region_faces = []
                    if self.inset > 0.0:
                        inset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces, thickness=self.inset,
                                                                    use_even_offset=True, use_interpolate=True)
                        inset_region_faces = inset_region_faces["faces"]

                    new_selected_verts = []

                    for face in set(inset_region_faces+inset_faces):
                        for vert in face.verts:
                            if vert.co in selected_co:
                                new_selected_verts.append(vert)
                                selected_co.remove(vert.co)

                    selected_verts_fin.append(new_selected_verts)
                    select_only(object_bm, new_selected_verts, {"EDGE"})

                    if self.fill_type == "HOLE":
                        bmesh.ops.delete(object_bm, geom=inset_faces, context=5)
                        inset_faces = []
                    elif self.fill_type == "NGON":
                        inset_faces = [bmesh.utils.face_join(inset_faces)]

                    if self.fill_flatten and self.extrude == 0:
                        verts = list(set(reduce(lambda v1, v2: list(v1) + list(v2),
                                                [v.verts for v in inset_region_faces + inset_faces])))
                        matrix = Matrix.Translation(-center)
                        bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                        bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts)
                        bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)

                    bmesh.ops.recalc_face_normals(object_bm, faces=outset_region_faces+inset_region_faces+inset_faces)
                    if self.extrude != 0:
                        extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=inset_region_faces+inset_faces)
                        verts = [v for v in extrude_geom['geom'] if isinstance(v, bmesh.types.BMVert)]

                        if self.fill_flatten:
                            matrix = Matrix.Translation(-center)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts)
                            bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts)
                            bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts)
                        bmesh.ops.delete(object_bm, geom=inset_region_faces+inset_faces, context=5)
                        bmesh.ops.translate(object_bm,
                                            verts=verts,
                                            vec=forward * self.extrude)
            del shape_bm
        if selected_verts_fin:
            select_only(object_bm, reduce(lambda x, y: x + y, selected_verts_fin), {"EDGE"})
        object_bm.select_flush(True)
        del object_bvh
        bmesh.update_edit_mesh(object.data)
        return {'FINISHED'}