Cleanup: remove "Tweak" event type
[blender-addons.git] / add_mesh_extra_objects / add_mesh_supertoroid.py
blobe325cffc71fe114b285b2e830275405dbce47263
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Author: DreamPainter
5 import bpy
6 from bpy.props import (
7 FloatProperty,
8 BoolProperty,
9 IntProperty,
10 StringProperty,
12 from math import pi, cos, sin
13 from mathutils import Vector
14 from bpy_extras import object_utils
17 # A very simple "bridge" tool
19 def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
20 faces = []
22 if not vertIdx1 or not vertIdx2:
23 return None
25 if len(vertIdx1) < 2 and len(vertIdx2) < 2:
26 return None
28 fan = False
29 if (len(vertIdx1) != len(vertIdx2)):
30 if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
31 fan = True
32 else:
33 return None
35 total = len(vertIdx2)
37 if closed:
38 # Bridge the start with the end.
39 if flipped:
40 face = [
41 vertIdx1[0],
42 vertIdx2[0],
43 vertIdx2[total - 1]]
44 if not fan:
45 face.append(vertIdx1[total - 1])
46 faces.append(face)
48 else:
49 face = [vertIdx2[0], vertIdx1[0]]
50 if not fan:
51 face.append(vertIdx1[total - 1])
52 face.append(vertIdx2[total - 1])
53 faces.append(face)
55 # Bridge the rest of the faces.
56 for num in range(total - 1):
57 if flipped:
58 if fan:
59 face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
60 else:
61 face = [vertIdx2[num], vertIdx1[num],
62 vertIdx1[num + 1], vertIdx2[num + 1]]
63 faces.append(face)
64 else:
65 if fan:
66 face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
67 else:
68 face = [vertIdx1[num], vertIdx2[num],
69 vertIdx2[num + 1], vertIdx1[num + 1]]
70 faces.append(face)
72 return faces
75 def power(a, b):
76 if a < 0:
77 return -((-a) ** b)
78 return a ** b
81 def supertoroid(R, r, u, v, n1, n2):
82 """
83 R = big radius
84 r = small radius
85 u = lateral segmentation
86 v = radial segmentation
87 n1 = value determines the shape of the torus
88 n2 = value determines the shape of the cross-section
89 """
90 # create the necessary constants
91 a = 2 * pi / u
92 b = 2 * pi / v
94 verts = []
95 faces = []
97 # create each cross-section by calculating each vector on the
98 # the wannabe circle
99 # x = (cos(theta) ** n1)*(R + r * (cos(phi) ** n2))
100 # y = (sin(theta) ** n1)*(R + r * (cos(phi) ** n2))
101 # z = (r * sin(phi) ** n2)
102 # with theta and phi rangeing from 0 to 2pi
104 for i in range(u):
105 s = power(sin(i * a), n1)
106 c = power(cos(i * a), n1)
107 for j in range(v):
108 c2 = R + r * power(cos(j * b), n2)
109 s2 = r * power(sin(j * b), n2)
110 verts.append(Vector((c * c2, s * c2, s2)))
112 # bridge the last circle with the previous circle
113 if i > 0: # but not for the first circle, 'cus there's no previous before the first
114 f = createFaces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True)
115 faces.extend(f)
116 # bridge the last circle with the first
117 f = createFaces(range((u - 1) * v, u * v), range(v), closed=True)
118 faces.extend(f)
120 return verts, faces
123 class add_supertoroid(bpy.types.Operator, object_utils.AddObjectHelper):
124 bl_idname = "mesh.primitive_supertoroid_add"
125 bl_label = "Add SuperToroid"
126 bl_description = "Construct a supertoroid mesh"
127 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
129 SuperToroid : BoolProperty(name = "SuperToroid",
130 default = True,
131 description = "SuperToroid")
132 change : BoolProperty(name = "Change",
133 default = False,
134 description = "change SuperToroid")
136 R: FloatProperty(
137 name="Big radius",
138 description="The radius inside the tube",
139 default=1.0,
140 min=0.01, max=100.0
142 r: FloatProperty(
143 name="Small radius",
144 description="The radius of the tube",
145 default=0.3,
146 min=0.01, max=100.0
148 u: IntProperty(
149 name="U-segments",
150 description="Radial segmentation",
151 default=16,
152 min=3, max=265
154 v: IntProperty(
155 name="V-segments",
156 description="Lateral segmentation",
157 default=8,
158 min=3, max=265
160 n1: FloatProperty(
161 name="Ring manipulator",
162 description="Manipulates the shape of the Ring",
163 default=1.0,
164 min=0.01, max=100.0
166 n2: FloatProperty(
167 name="Cross manipulator",
168 description="Manipulates the shape of the cross-section",
169 default=1.0,
170 min=0.01, max=100.0
172 ie: BoolProperty(
173 name="Use Int. and Ext. radii",
174 description="Use internal and external radii",
175 default=False
177 edit: BoolProperty(
178 name="",
179 description="",
180 default=False,
181 options={'HIDDEN'}
184 def draw(self, context):
185 layout = self.layout
187 layout.prop(self, 'R', expand=True)
188 layout.prop(self, 'r', expand=True)
189 layout.prop(self, 'u', expand=True)
190 layout.prop(self, 'v', expand=True)
191 layout.prop(self, 'n1', expand=True)
192 layout.prop(self, 'n2', expand=True)
193 layout.prop(self, 'ie', expand=True)
195 if self.change == False:
196 col = layout.column(align=True)
197 col.prop(self, 'align', expand=True)
198 col = layout.column(align=True)
199 col.prop(self, 'location', expand=True)
200 col = layout.column(align=True)
201 col.prop(self, 'rotation', expand=True)
203 def execute(self, context):
204 # turn off 'Enter Edit Mode'
205 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
206 bpy.context.preferences.edit.use_enter_edit_mode = False
208 props = self.properties
210 # check how the radii properties must be used
211 if props.ie:
212 rad1 = (props.R + props.r) / 2
213 rad2 = (props.R - props.r) / 2
214 # for consistency in the mesh, ie no crossing faces, make the largest of the two
215 # the outer radius
216 if rad2 > rad1:
217 [rad1, rad2] = [rad2, rad1]
218 else:
219 rad1 = props.R
220 rad2 = props.r
221 # again for consistency, make the radius in the tube,
222 # at least as big as the radius of the tube
223 if rad2 > rad1:
224 rad1 = rad2
226 if bpy.context.mode == "OBJECT":
227 if context.selected_objects != [] and context.active_object and \
228 (context.active_object.data is not None) and ('SuperToroid' in context.active_object.data.keys()) and \
229 (self.change == True):
230 obj = context.active_object
231 oldmesh = obj.data
232 oldmeshname = obj.data.name
233 verts, faces = supertoroid(rad1,
234 rad2,
235 props.u,
236 props.v,
237 props.n1,
238 props.n2
240 mesh = bpy.data.meshes.new('SuperToroid')
241 mesh.from_pydata(verts, [], faces)
242 obj.data = mesh
243 for material in oldmesh.materials:
244 obj.data.materials.append(material)
245 bpy.data.meshes.remove(oldmesh)
246 obj.data.name = oldmeshname
247 else:
248 verts, faces = supertoroid(rad1,
249 rad2,
250 props.u,
251 props.v,
252 props.n1,
253 props.n2
255 mesh = bpy.data.meshes.new('SuperToroid')
256 mesh.from_pydata(verts, [], faces)
257 obj = object_utils.object_data_add(context, mesh, operator=self)
259 obj.data["SuperToroid"] = True
260 obj.data["change"] = False
261 for prm in SuperToroidParameters():
262 obj.data[prm] = getattr(self, prm)
264 if bpy.context.mode == "EDIT_MESH":
265 active_object = context.active_object
266 name_active_object = active_object.name
267 bpy.ops.object.mode_set(mode='OBJECT')
268 verts, faces = supertoroid(rad1,
269 rad2,
270 props.u,
271 props.v,
272 props.n1,
273 props.n2
275 mesh = bpy.data.meshes.new('SuperToroid')
276 mesh.from_pydata(verts, [], faces)
277 obj = object_utils.object_data_add(context, mesh, operator=self)
278 obj.select_set(True)
279 active_object.select_set(True)
280 bpy.context.view_layer.objects.active = active_object
281 bpy.ops.object.join()
282 context.active_object.name = name_active_object
283 bpy.ops.object.mode_set(mode='EDIT')
285 if use_enter_edit_mode:
286 bpy.ops.object.mode_set(mode = 'EDIT')
288 # restore pre operator state
289 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
291 return {'FINISHED'}
293 def SuperToroidParameters():
294 SuperToroidParameters = [
295 "R",
296 "r",
297 "u",
298 "v",
299 "n1",
300 "n2",
301 "ie",
302 "edit",
304 return SuperToroidParameters