Sun position: Fix T80379 - Custom startup breaks the add-on
[blender-addons.git] / add_mesh_extra_objects / add_mesh_triangles.py
blob5373c5fb4a31d23152c50b9b9b606d084e87221d
1 # GPL # "author": Sjaak-de-Draak
3 bl_info = {
4 "name": "Triangles",
5 "description": "Create different types of triangles",
6 "author": "Sjaak-de-Draak",
7 "version": (1, 0, 1),
8 "blender": (2, 68, 0),
9 "location": "View3D > Add > Mesh",
10 "warning": "First Version",
11 "doc_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
12 "Scripts/Triangles",
13 "category": "Add Mesh",
16 """
17 This script provides a triangle mesh primitive
18 and a toolbar menu to further specify settings
19 """
21 import math
22 import bpy
23 from mathutils import Vector
24 from bpy.types import Operator
25 from bpy.props import (
26 BoolProperty,
27 EnumProperty,
28 FloatProperty,
32 def checkEditMode():
33 # Check if we are in edit mode
34 # Returns: 1 if True
35 # 0 if False
36 if (bpy.context.active_object.mode == 'EDIT'):
37 return 1
38 return 0
41 def exitEditMode():
42 # Check if we are in edit mode (cuz we don't want this when creating a new Mesh)
43 # If we are then toggle back to object mode
44 # Check if there are active objects
45 if bpy.context.active_object is not None:
46 # Only the active object should be in edit mode
47 if (bpy.context.active_object.mode == 'EDIT'):
48 bpy.ops.object.editmode_toggle()
51 class MakeTriangle(Operator):
52 bl_idname = "mesh.make_triangle"
53 bl_label = "Triangle"
54 bl_description = "Construct different types of Triangle Meshes"
55 bl_options = {"REGISTER", "UNDO"}
57 nothing = 0
58 Ya = 0.0
59 Xb = 0.0
60 Xc = 0.0
61 Vertices = []
62 Faces = []
64 triangleTypeList = [
65 ('ISOSCELES', "Isosceles", "Two equal sides", 0),
66 ('EQUILATERAL', "Equilateral", "Three equal sides and angles (60°)", 1),
67 ('ISOSCELESRIGHTANGLE', "Isosceles right angled", "90° angle and two equal sides", 2),
68 ('SCALENERIGHTANGLE', "Scalene right angled", "90° angle, no equal sides", 3)
70 triangleFaceList = [
71 ('DEFAULT', "Normal", "1 Tri(angle) face", 0),
72 ('TRIANGLES', "3 Tri faces", "4 Vertices & 3 Tri(angle) faces", 1),
73 ('QUADS', "3 Quad faces", "7 Vertices & 3 Quad faces", 2),
74 ('SAFEQUADS', "6 Quad faces", "12 Vertices & 6 Quad faces", 3)
77 # add definitions for some manipulation buttons
78 flipX: BoolProperty(
79 name="Flip X sign",
80 description="Draw on the other side of the X axis (Mirror on Y axis)",
81 default=False
83 flipY: BoolProperty(
84 name="Flip Y sign",
85 description="Draw on the other side of the Y axis (Mirror on X axis)",
86 default=False
88 scale: FloatProperty(
89 name="Scale",
90 description="Triangle scale",
91 default=1.0,
92 min=1.0
94 triangleType: EnumProperty(
95 items=triangleTypeList,
96 name="Type",
97 description="Triangle Type"
99 triangleFace: EnumProperty(
100 items=triangleFaceList,
101 name="Face types",
102 description="Triangle Face Types"
104 at_3Dcursor: BoolProperty(
105 name="Use 3D Cursor",
106 description="Draw the triangle where the 3D cursor is",
107 default=False
110 def draw(self, context):
111 layout = self.layout
113 col = layout.column(align=True)
114 col.prop(self, "triangleType", text="Type")
115 col.prop(self, "scale")
116 col.prop(self, "triangleFace", text="Face")
118 col = layout.column(align=True)
119 col.prop(self, "at_3Dcursor", text="3D Cursor", toggle=True)
121 row = col.row(align=True)
122 row.prop(self, "flipX", toggle=True)
123 row.prop(self, "flipY", toggle=True)
125 def drawBasicTriangleShape(self):
126 # set everything to 0
127 Xb = Xc = 0.0
128 Ya = 0.0
130 scale = self.scale
131 Xsign = -1 if self.flipX else 1
132 Ysign = -1 if self.flipY else 1
134 # Isosceles (2 equal sides)
135 if (self.triangleType == 'ISOSCELES'):
136 # below a simple triangle containing 2 triangles with 1:2 side ratio
137 Ya = (1 * Ysign * scale)
138 A = Vector([0.0, Ya, 0.0])
139 Xb = (0.5 * Xsign * scale)
140 B = Vector([Xb, 0.0, 0.0])
141 Xc = (-0.5 * Xsign * scale)
142 C = Vector([Xc, 0.0, 0.0])
144 self.Ya = Ya
145 self.Xb = Xb
146 self.Xc = Xc
147 self.Vertices = [A, B, C, ]
149 return True
151 # Equilateral (all sides equal)
152 if (self.triangleType == 'EQUILATERAL'):
153 Ya = (math.sqrt(0.75) * Ysign * scale)
154 A = Vector([0.0, Ya, 0.0])
155 Xb = (0.5 * Xsign * scale)
156 B = Vector([Xb, 0.0, 0.0])
157 Xc = (-0.5 * Xsign * scale)
158 C = Vector([Xc, 0.0, 0.0])
160 self.Ya = Ya
161 self.Xb = Xb
162 self.Xc = Xc
163 self.Vertices = [A, B, C, ]
165 return True
167 # Isosceles right angled (1, 1, sqrt(2))
168 if (self.triangleType == 'ISOSCELESRIGHTANGLE'):
169 Ya = (1 * Ysign * scale)
170 A = Vector([0.0, Ya, 0.0])
171 Xb = 0.0
172 B = Vector([Xb, 0.0, 0.0])
173 Xc = (1 * Xsign * scale)
174 C = Vector([Xc, 0.0, 0.0])
176 self.Ya = Ya
177 self.Xb = Xb
178 self.Xc = Xc
179 self.Vertices = [A, B, C, ]
180 return True
182 # Scalene right angled (3, 4, 5)
183 if (self.triangleType == 'SCALENERIGHTANGLE'):
184 Ya = (1 * Ysign * scale)
185 A = Vector([0.0, Ya, 0.0])
186 Xb = 0
187 B = Vector([Xb, 0.0, 0.0])
188 Xc = (0.75 * Xsign * scale)
189 C = Vector([Xc, 0.0, 0.0])
191 self.Ya = Ya
192 self.Xb = Xb
193 self.Xc = Xc
194 self.Vertices = [A, B, C, ]
195 return True
197 return False
199 def addFaces(self, fType=None):
200 Ya = self.Ya
201 Xb = self.Xb
202 Xc = self.Xc
204 if (self.triangleFace == 'DEFAULT'):
205 self.Faces = [[0, 1, 2]]
206 return True
208 if (self.triangleFace == 'TRIANGLES'):
209 A = Vector([0.0, Ya, 0.0])
210 B = Vector([Xb, 0.0, 0.0])
211 C = Vector([Xc, 0.0, 0.0])
212 D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
214 self.Vertices = [A, B, C, D, ]
215 self.Faces = [[0, 1, 3], [1, 2, 3], [2, 0, 3]]
216 return True
218 if (self.triangleFace == 'QUADS'):
219 A = Vector([0.0, Ya, 0.0])
220 B = Vector([Xb, 0.0, 0.0])
221 C = Vector([Xc, 0.0, 0.0])
222 D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
223 AB = A.lerp(B, 0.5)
224 AC = A.lerp(C, 0.5)
225 BC = B.lerp(C, 0.5)
227 self.Vertices = [A, AB, B, BC, C, AC, D, ]
228 self.Faces = [[0, 1, 6, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
229 return True
231 if (self.triangleFace == 'SAFEQUADS'):
232 A = Vector([0.0, Ya, 0.0])
233 B = Vector([Xb, 0.0, 0.0])
234 C = Vector([Xc, 0.0, 0.0])
235 D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
236 E = A.lerp(D, 0.5)
237 AB = A.lerp(B, 0.5)
238 AC = A.lerp(C, 0.5)
239 BC = B.lerp(C, 0.5)
240 AAB = AB.lerp(A, 0.5)
241 AAC = AC.lerp(A, 0.5)
242 BBA = AB.lerp(B, 0.5)
243 BBC = BC.lerp(B, 0.5)
244 BCC = BC.lerp(C, 0.5)
245 CCA = AC.lerp(C, 0.5)
247 self.Vertices = [A, AAB, BBA, B, BBC, BC, BCC, C, CCA, AAC, D, E, ]
248 self.Faces = [[0, 1, 11, 9], [1, 2, 10, 11], [2, 3, 4, 10],
249 [4, 5, 6, 10], [6, 7, 8, 10], [8, 9, 11, 10]]
250 return True
252 return False
254 def action_common(self, context):
255 # definitions:
256 # a triangle consists of 3 points: A, B, C
257 # a 'safer' subdividable triangle consists of 4 points: A, B, C, D
258 # a subdivide friendly triangle consists of 7 points: A, B, C, D, AB, AC, BC
259 # a truly subdivide friendly triangle consists of (3 x 4 = )12 points:
260 # A, B, C, D, E, BC, AAB, AAC, BBA, BBC, BCC, CCA
262 BasicShapeCreated = False
263 ShapeFacesCreated = False
264 go = 0
267 # call the functions for creating the triangles and test if successful
269 BasicShapeCreated = self.drawBasicTriangleShape()
270 if (BasicShapeCreated):
271 ShapeFacesCreated = self.addFaces()
272 if ShapeFacesCreated:
273 go = 1
275 if (go == 1):
276 NewMesh = bpy.data.meshes.new("Triangle")
277 NewMesh.from_pydata(self.Vertices, [], self.Faces)
279 NewMesh.update()
280 NewObj = bpy.data.objects.new("Triangle", NewMesh)
281 context.collection.objects.link(NewObj)
283 # before doing the deselect make sure edit mode isn't active
284 exitEditMode()
285 bpy.ops.object.select_all(action="DESELECT")
286 NewObj.select_set(True)
287 context.view_layer.objects.active = NewObj
289 if self.at_3Dcursor is True:
290 # we'll need to be sure there is actually an object selected
291 if NewObj.select_get() is True:
292 # we also have to check if we're considered to be in 3D View (view3d)
293 if bpy.ops.view3d.snap_selected_to_cursor.poll() is True:
294 bpy.ops.view3d.snap_selected_to_cursor()
295 else:
296 # as we weren't considered to be in 3D View
297 # the object couldn't be moved to the 3D cursor
298 # so to avoid confusion we change the at_3Dcursor boolean to false
299 self.at_3Dcursor = False
301 else:
302 self.report({'WARNING'},
303 "Triangle could not be completed. (See Console for more Info)")
305 print("\n[Add Mesh Extra Objects]\n\nModule: add_mesh_triangle")
306 print("Triangle type: %s\n" % self.triangleType,
307 "Face type: %s\n" % self.triangleFace,
308 "Ya: %s, Xb: %s, Xc: %s\n" % (self.Ya, self.Xb, self.Xc),
309 "Vertices: %s\n" % self.Vertices,
310 "Faces: %s\n" % self.Faces)
312 def execute(self, context):
313 self.action_common(context)
314 return {"FINISHED"}
316 def invoke(self, context, event):
317 self.action_common(context)
318 return {"FINISHED"}