Fix #105067: Node Wrangler: cannot preview multi-user material group
[blender-addons.git] / add_mesh_extra_objects / add_mesh_triangles.py
blob2824a917723ce80c2921569b531d262f4ae06f53
1 # SPDX-FileCopyrightText: 2017-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 "name": "Triangles",
7 "description": "Create different types of triangles",
8 "author": "Sjaak-de-Draak",
9 "version": (1, 0, 1),
10 "blender": (2, 68, 0),
11 "location": "View3D > Add > Mesh",
12 "warning": "First Version",
13 "doc_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
14 "Scripts/Triangles",
15 "category": "Add Mesh",
18 """
19 This script provides a triangle mesh primitive
20 and a toolbar menu to further specify settings
21 """
23 import math
24 import bpy
25 from mathutils import Vector
26 from bpy.types import Operator
27 from bpy.props import (
28 BoolProperty,
29 EnumProperty,
30 FloatProperty,
34 def checkEditMode():
35 # Check if we are in edit mode
36 # Returns: 1 if True
37 # 0 if False
38 if (bpy.context.active_object.mode == 'EDIT'):
39 return 1
40 return 0
43 def exitEditMode():
44 # Check if we are in edit mode (cuz we don't want this when creating a new Mesh)
45 # If we are then toggle back to object mode
46 # Check if there are active objects
47 if bpy.context.active_object is not None:
48 # Only the active object should be in edit mode
49 if (bpy.context.active_object.mode == 'EDIT'):
50 bpy.ops.object.editmode_toggle()
53 class MakeTriangle(Operator):
54 bl_idname = "mesh.make_triangle"
55 bl_label = "Triangle"
56 bl_description = "Construct different types of Triangle Meshes"
57 bl_options = {"REGISTER", "UNDO"}
59 nothing = 0
60 Ya = 0.0
61 Xb = 0.0
62 Xc = 0.0
63 Vertices = []
64 Faces = []
66 triangleTypeList = [
67 ('ISOSCELES', "Isosceles", "Two equal sides", 0),
68 ('EQUILATERAL', "Equilateral", "Three equal sides and angles (60°)", 1),
69 ('ISOSCELESRIGHTANGLE', "Isosceles right angled", "90° angle and two equal sides", 2),
70 ('SCALENERIGHTANGLE', "Scalene right angled", "90° angle, no equal sides", 3)
72 triangleFaceList = [
73 ('DEFAULT', "Normal", "1 Tri(angle) face", 0),
74 ('TRIANGLES', "3 Tri faces", "4 Vertices & 3 Tri(angle) faces", 1),
75 ('QUADS', "3 Quad faces", "7 Vertices & 3 Quad faces", 2),
76 ('SAFEQUADS', "6 Quad faces", "12 Vertices & 6 Quad faces", 3)
79 # add definitions for some manipulation buttons
80 flipX: BoolProperty(
81 name="Flip X sign",
82 description="Draw on the other side of the X axis (Mirror on Y axis)",
83 default=False
85 flipY: BoolProperty(
86 name="Flip Y sign",
87 description="Draw on the other side of the Y axis (Mirror on X axis)",
88 default=False
90 scale: FloatProperty(
91 name="Scale",
92 description="Triangle scale",
93 default=1.0,
94 min=1.0
96 triangleType: EnumProperty(
97 items=triangleTypeList,
98 name="Type",
99 description="Triangle Type"
101 triangleFace: EnumProperty(
102 items=triangleFaceList,
103 name="Face types",
104 description="Triangle Face Types"
106 at_3Dcursor: BoolProperty(
107 name="Use 3D Cursor",
108 description="Draw the triangle where the 3D cursor is",
109 default=False
112 def draw(self, context):
113 layout = self.layout
115 col = layout.column(align=True)
116 col.prop(self, "triangleType", text="Type")
117 col.prop(self, "scale")
118 col.prop(self, "triangleFace", text="Face")
120 col = layout.column(align=True)
121 col.prop(self, "at_3Dcursor", text="3D Cursor", toggle=True)
123 row = col.row(align=True)
124 row.prop(self, "flipX", toggle=True)
125 row.prop(self, "flipY", toggle=True)
127 def drawBasicTriangleShape(self):
128 # set everything to 0
129 Xb = Xc = 0.0
130 Ya = 0.0
132 scale = self.scale
133 Xsign = -1 if self.flipX else 1
134 Ysign = -1 if self.flipY else 1
136 # Isosceles (2 equal sides)
137 if (self.triangleType == 'ISOSCELES'):
138 # below a simple triangle containing 2 triangles with 1:2 side ratio
139 Ya = (1 * Ysign * scale)
140 A = Vector([0.0, Ya, 0.0])
141 Xb = (0.5 * Xsign * scale)
142 B = Vector([Xb, 0.0, 0.0])
143 Xc = (-0.5 * Xsign * scale)
144 C = Vector([Xc, 0.0, 0.0])
146 self.Ya = Ya
147 self.Xb = Xb
148 self.Xc = Xc
149 self.Vertices = [A, B, C, ]
151 return True
153 # Equilateral (all sides equal)
154 if (self.triangleType == 'EQUILATERAL'):
155 Ya = (math.sqrt(0.75) * Ysign * scale)
156 A = Vector([0.0, Ya, 0.0])
157 Xb = (0.5 * Xsign * scale)
158 B = Vector([Xb, 0.0, 0.0])
159 Xc = (-0.5 * Xsign * scale)
160 C = Vector([Xc, 0.0, 0.0])
162 self.Ya = Ya
163 self.Xb = Xb
164 self.Xc = Xc
165 self.Vertices = [A, B, C, ]
167 return True
169 # Isosceles right angled (1, 1, sqrt(2))
170 if (self.triangleType == 'ISOSCELESRIGHTANGLE'):
171 Ya = (1 * Ysign * scale)
172 A = Vector([0.0, Ya, 0.0])
173 Xb = 0.0
174 B = Vector([Xb, 0.0, 0.0])
175 Xc = (1 * Xsign * scale)
176 C = Vector([Xc, 0.0, 0.0])
178 self.Ya = Ya
179 self.Xb = Xb
180 self.Xc = Xc
181 self.Vertices = [A, B, C, ]
182 return True
184 # Scalene right angled (3, 4, 5)
185 if (self.triangleType == 'SCALENERIGHTANGLE'):
186 Ya = (1 * Ysign * scale)
187 A = Vector([0.0, Ya, 0.0])
188 Xb = 0
189 B = Vector([Xb, 0.0, 0.0])
190 Xc = (0.75 * Xsign * scale)
191 C = Vector([Xc, 0.0, 0.0])
193 self.Ya = Ya
194 self.Xb = Xb
195 self.Xc = Xc
196 self.Vertices = [A, B, C, ]
197 return True
199 return False
201 def addFaces(self, fType=None):
202 Ya = self.Ya
203 Xb = self.Xb
204 Xc = self.Xc
206 if (self.triangleFace == 'DEFAULT'):
207 self.Faces = [[0, 1, 2]]
208 return True
210 if (self.triangleFace == 'TRIANGLES'):
211 A = Vector([0.0, Ya, 0.0])
212 B = Vector([Xb, 0.0, 0.0])
213 C = Vector([Xc, 0.0, 0.0])
214 D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
216 self.Vertices = [A, B, C, D, ]
217 self.Faces = [[0, 1, 3], [1, 2, 3], [2, 0, 3]]
218 return True
220 if (self.triangleFace == 'QUADS'):
221 A = Vector([0.0, Ya, 0.0])
222 B = Vector([Xb, 0.0, 0.0])
223 C = Vector([Xc, 0.0, 0.0])
224 D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
225 AB = A.lerp(B, 0.5)
226 AC = A.lerp(C, 0.5)
227 BC = B.lerp(C, 0.5)
229 self.Vertices = [A, AB, B, BC, C, AC, D, ]
230 self.Faces = [[0, 1, 6, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
231 return True
233 if (self.triangleFace == 'SAFEQUADS'):
234 A = Vector([0.0, Ya, 0.0])
235 B = Vector([Xb, 0.0, 0.0])
236 C = Vector([Xc, 0.0, 0.0])
237 D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
238 E = A.lerp(D, 0.5)
239 AB = A.lerp(B, 0.5)
240 AC = A.lerp(C, 0.5)
241 BC = B.lerp(C, 0.5)
242 AAB = AB.lerp(A, 0.5)
243 AAC = AC.lerp(A, 0.5)
244 BBA = AB.lerp(B, 0.5)
245 BBC = BC.lerp(B, 0.5)
246 BCC = BC.lerp(C, 0.5)
247 CCA = AC.lerp(C, 0.5)
249 self.Vertices = [A, AAB, BBA, B, BBC, BC, BCC, C, CCA, AAC, D, E, ]
250 self.Faces = [[0, 1, 11, 9], [1, 2, 10, 11], [2, 3, 4, 10],
251 [4, 5, 6, 10], [6, 7, 8, 10], [8, 9, 11, 10]]
252 return True
254 return False
256 def action_common(self, context):
257 # definitions:
258 # a triangle consists of 3 points: A, B, C
259 # a 'safer' subdividable triangle consists of 4 points: A, B, C, D
260 # a subdivide friendly triangle consists of 7 points: A, B, C, D, AB, AC, BC
261 # a truly subdivide friendly triangle consists of (3 x 4 = )12 points:
262 # A, B, C, D, E, BC, AAB, AAC, BBA, BBC, BCC, CCA
264 BasicShapeCreated = False
265 ShapeFacesCreated = False
266 go = 0
269 # call the functions for creating the triangles and test if successful
271 BasicShapeCreated = self.drawBasicTriangleShape()
272 if (BasicShapeCreated):
273 ShapeFacesCreated = self.addFaces()
274 if ShapeFacesCreated:
275 go = 1
277 if (go == 1):
278 NewMesh = bpy.data.meshes.new("Triangle")
279 NewMesh.from_pydata(self.Vertices, [], self.Faces)
281 NewMesh.update()
282 NewObj = bpy.data.objects.new("Triangle", NewMesh)
283 context.collection.objects.link(NewObj)
285 # before doing the deselect make sure edit mode isn't active
286 exitEditMode()
287 bpy.ops.object.select_all(action="DESELECT")
288 NewObj.select_set(True)
289 context.view_layer.objects.active = NewObj
291 if self.at_3Dcursor is True:
292 # we'll need to be sure there is actually an object selected
293 if NewObj.select_get() is True:
294 # we also have to check if we're considered to be in 3D View (view3d)
295 if bpy.ops.view3d.snap_selected_to_cursor.poll() is True:
296 bpy.ops.view3d.snap_selected_to_cursor()
297 else:
298 # as we weren't considered to be in 3D View
299 # the object couldn't be moved to the 3D cursor
300 # so to avoid confusion we change the at_3Dcursor boolean to false
301 self.at_3Dcursor = False
303 else:
304 self.report({'WARNING'},
305 "Triangle could not be completed. (See Console for more Info)")
307 print("\n[Add Mesh Extra Objects]\n\nModule: add_mesh_triangle")
308 print("Triangle type: %s\n" % self.triangleType,
309 "Face type: %s\n" % self.triangleFace,
310 "Ya: %s, Xb: %s, Xc: %s\n" % (self.Ya, self.Xb, self.Xc),
311 "Vertices: %s\n" % self.Vertices,
312 "Faces: %s\n" % self.Faces)
314 def execute(self, context):
315 self.action_common(context)
316 return {"FINISHED"}
318 def invoke(self, context, event):
319 self.action_common(context)
320 return {"FINISHED"}