Skip to content
Snippets Groups Projects
add_mesh_triangles.py 10.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • # GPL # "author": Sjaak-de-Draak
    
    
    bl_info = {
        "name": "Triangles",
    
        "description": "Create different types of triangles",
    
        "author": "Sjaak-de-Draak",
    
        "blender": (2, 68, 0),
        "location": "View3D > Add > Mesh",
    
        "warning": "First Version",
        "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
    
                    "Scripts/Triangles",
        "category": "Add Mesh"}
    
    """
    This script provides a triangle mesh primitive
    and a toolbar menu to further specify settings
    """
    
    import math
    import bpy
    
    from mathutils import Vector
    from bpy.types import Operator
    from bpy.props import (
            BoolProperty,
            EnumProperty,
            FloatProperty,
            )
    
        # Check if we are in edit mode
        # Returns:  1 if True
        #           0 if False
    
        if (bpy.context.active_object.mode == 'EDIT'):
    
        # Check if we are in edit mode (cuz we don't want this when creating a new Mesh)
        # If we are then toggle back to object mode
    
        # Check if there are active objects
    
        if bpy.context.active_object is not None:
            # Only the active object should be in edit mode
    
            if (bpy.context.active_object.mode == 'EDIT'):
                bpy.ops.object.editmode_toggle()
    
    
    
    class MakeTriangle(Operator):
    
        bl_idname = "mesh.make_triangle"
        bl_label = "Triangle"
    
        bl_description = "Construct different types of Triangle Meshes"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        triangleTypeList = [
                ('ISOSCELES', "Isosceles", "Two equal sides", 0),
    
                ('EQUILATERAL', "Equilateral", "Three equal sides and angles (60°)", 1),
                ('ISOSCELESRIGHTANGLE', "Isosceles right angled", "90° angle and two equal sides", 2),
    
                ('SCALENERIGHTANGLE', "Scalene right angled", "90° angle, no equal sides", 3)
                ]
        triangleFaceList = [
                ('DEFAULT', "Normal", "1 Tri(angle) face", 0),
    
                ('TRIANGLES', "3 Tri faces", "4 Vertices & 3 Tri(angle) faces", 1),
                ('QUADS', "3 Quad faces", "7 Vertices & 3 Quad faces", 2),
                ('SAFEQUADS', "6 Quad faces", "12 Vertices & 6 Quad faces", 3)
    
    
        # add definitions for some manipulation buttons
    
        flipX: BoolProperty(
    
                name="Flip X sign",
                description="Draw on the other side of the X axis (Mirror on Y axis)",
                default=False
                )
    
        flipY: BoolProperty(
    
                name="Flip Y sign",
                description="Draw on the other side of the Y axis (Mirror on X axis)",
                default=False
                )
    
        scale: FloatProperty(
    
                name="Scale",
                description="Triangle scale",
                default=1.0,
                min=1.0
                )
    
        triangleType: EnumProperty(
    
                items=triangleTypeList,
                name="Type",
                description="Triangle Type"
                )
    
        triangleFace: EnumProperty(
    
                items=triangleFaceList,
                name="Face types",
                description="Triangle Face Types"
                )
    
        at_3Dcursor: BoolProperty(
    
                name="Use 3D Cursor",
                description="Draw the triangle where the 3D cursor is",
                default=False
                )
    
    
        def draw(self, context):
            layout = self.layout
    
    
            col = layout.column(align=True)
            col.prop(self, "triangleType", text="Type")
            col.prop(self, "scale")
            col.prop(self, "triangleFace", text="Face")
    
    
            col = layout.column(align=True)
    
            col.prop(self, "at_3Dcursor", text="3D Cursor", toggle=True)
    
            row = col.row(align=True)
            row.prop(self, "flipX", toggle=True)
            row.prop(self, "flipY", toggle=True)
    
    
        def drawBasicTriangleShape(self):
            # set everything to 0
    
            Xsign = -1 if self.flipX else 1
            Ysign = -1 if self.flipY else 1
    
            # Isosceles (2 equal sides)
    
            if (self.triangleType == 'ISOSCELES'):
    
                # below a simple triangle containing 2 triangles with 1:2 side ratio
    
                Ya = (1 * Ysign * scale)
                A = Vector([0.0, Ya, 0.0])
                Xb = (0.5 * Xsign * scale)
                B = Vector([Xb, 0.0, 0.0])
                Xc = (-0.5 * Xsign * scale)
                C = Vector([Xc, 0.0, 0.0])
    
                self.Ya = Ya
                self.Xb = Xb
                self.Xc = Xc
                self.Vertices = [A, B, C, ]
    
    
                return True
    
            # Equilateral (all sides equal)
    
            if (self.triangleType == 'EQUILATERAL'):
                Ya = (math.sqrt(0.75) * Ysign * scale)
                A = Vector([0.0, Ya, 0.0])
                Xb = (0.5 * Xsign * scale)
                B = Vector([Xb, 0.0, 0.0])
                Xc = (-0.5 * Xsign * scale)
                C = Vector([Xc, 0.0, 0.0])
    
                self.Ya = Ya
                self.Xb = Xb
                self.Xc = Xc
                self.Vertices = [A, B, C, ]
    
    
            # Isosceles right angled (1, 1, sqrt(2))
            if (self.triangleType == 'ISOSCELESRIGHTANGLE'):
                Ya = (1 * Ysign * scale)
                A = Vector([0.0, Ya, 0.0])
                Xb = 0.0
                B = Vector([Xb, 0.0, 0.0])
                Xc = (1 * Xsign * scale)
                C = Vector([Xc, 0.0, 0.0])
    
                self.Ya = Ya
                self.Xb = Xb
                self.Xc = Xc
                self.Vertices = [A, B, C, ]
    
            # Scalene right angled (3, 4, 5)
            if (self.triangleType == 'SCALENERIGHTANGLE'):
                Ya = (1 * Ysign * scale)
                A = Vector([0.0, Ya, 0.0])
                Xb = 0
                B = Vector([Xb, 0.0, 0.0])
                Xc = (0.75 * Xsign * scale)
                C = Vector([Xc, 0.0, 0.0])
    
                self.Ya = Ya
                self.Xb = Xb
                self.Xc = Xc
                self.Vertices = [A, B, C, ]
    
            return False
    
        def addFaces(self, fType=None):
    
            Ya = self.Ya
            Xb = self.Xb
            Xc = self.Xc
    
    
            if (self.triangleFace == 'DEFAULT'):
    
                return True
    
            if (self.triangleFace == 'TRIANGLES'):
    
                A = Vector([0.0, Ya, 0.0])
                B = Vector([Xb, 0.0, 0.0])
                C = Vector([Xc, 0.0, 0.0])
                D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
    
                self.Vertices = [A, B, C, D, ]
                self.Faces = [[0, 1, 3], [1, 2, 3], [2, 0, 3]]
    
                return True
    
            if (self.triangleFace == 'QUADS'):
    
                A = Vector([0.0, Ya, 0.0])
                B = Vector([Xb, 0.0, 0.0])
                C = Vector([Xc, 0.0, 0.0])
                D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
    
                AB = A.lerp(B, 0.5)
                AC = A.lerp(C, 0.5)
                BC = B.lerp(C, 0.5)
    
    
                self.Vertices = [A, AB, B, BC, C, AC, D, ]
                self.Faces = [[0, 1, 6, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
    
                return True
    
            if (self.triangleFace == 'SAFEQUADS'):
    
                A = Vector([0.0, Ya, 0.0])
                B = Vector([Xb, 0.0, 0.0])
                C = Vector([Xc, 0.0, 0.0])
                D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
    
                E = A.lerp(D, 0.5)
                AB = A.lerp(B, 0.5)
                AC = A.lerp(C, 0.5)
                BC = B.lerp(C, 0.5)
                AAB = AB.lerp(A, 0.5)
                AAC = AC.lerp(A, 0.5)
                BBA = AB.lerp(B, 0.5)
                BBC = BC.lerp(B, 0.5)
                BCC = BC.lerp(C, 0.5)
                CCA = AC.lerp(C, 0.5)
    
    
                self.Vertices = [A, AAB, BBA, B, BBC, BC, BCC, C, CCA, AAC, D, E, ]
                self.Faces = [[0, 1, 11, 9], [1, 2, 10, 11], [2, 3, 4, 10],
                              [4, 5, 6, 10], [6, 7, 8, 10], [8, 9, 11, 10]]
    
    
        def action_common(self, context):
            # definitions:
    
            # a triangle consists of 3 points: A, B, C
            # a 'safer' subdividable triangle consists of 4 points: A, B, C, D
            # a subdivide friendly triangle consists of 7 points: A, B, C, D, AB, AC, BC
    
            # a truly subdivide friendly triangle consists of (3 x 4 = )12 points:
    
            # A, B, C, D, E, BC, AAB, AAC, BBA, BBC, BCC, CCA
    
            BasicShapeCreated = False
            ShapeFacesCreated = False
            go = 0
    
            # call the functions for creating the triangles and test if successful
    
            BasicShapeCreated = self.drawBasicTriangleShape()
    
            if (BasicShapeCreated):
    
                ShapeFacesCreated = self.addFaces()
                if ShapeFacesCreated:
                    go = 1
    
    
            if (go == 1):
                NewMesh = bpy.data.meshes.new("Triangle")
                NewMesh.from_pydata(self.Vertices, [], self.Faces)
    
                NewMesh.update()
                NewObj = bpy.data.objects.new("Triangle", NewMesh)
    
                context.collection.objects.link(NewObj)
    
    
                # before doing the deselect make sure edit mode isn't active
                exitEditMode()
    
                bpy.ops.object.select_all(action="DESELECT")
    
                NewObj.select_set(True)
    
                context.view_layer.objects.active = NewObj
    
                    # we'll need to be sure there is actually an object selected
    
                    if NewObj.select_get() is True:
    
                        # we also have to check if we're considered to be in 3D View (view3d)
    
                        if bpy.ops.view3d.snap_selected_to_cursor.poll() is True:
    
                            bpy.ops.view3d.snap_selected_to_cursor()
                        else:
                            # as we weren't considered to be in 3D View
                            # the object couldn't be moved to the 3D cursor
                            # so to avoid confusion we change the at_3Dcursor boolean to false
                            self.at_3Dcursor = False
    
                self.report({'WARNING'},
                            "Triangle could not be completed. (See Console for more Info)")
    
                print("\n[Add Mesh Extra Objects]\n\nModule: add_mesh_triangle")
                print("Triangle type: %s\n" % self.triangleType,
                      "Face type: %s\n" % self.triangleFace,
                      "Ya: %s, Xb: %s, Xc: %s\n" % (self.Ya, self.Xb, self.Xc),
                      "Vertices: %s\n" % self.Vertices,
                      "Faces: %s\n" % self.Faces)
    
        def execute(self, context):
    
            self.action_common(context)
            return {"FINISHED"}
    
    
        def invoke(self, context, event):
    
            self.action_common(context)
            return {"FINISHED"}