Cleanup: strip trailing space, remove BOM
[blender-addons.git] / add_mesh_extra_objects / add_mesh_twisted_torus.py
blobbb3bd48385066db64bb0d897484f3e724e7da749
1 # GPL # "author": Paulo_Gomes
3 import bpy
4 from mathutils import Quaternion, Vector
5 from math import cos, sin, pi
6 from bpy.props import (
7 FloatProperty,
8 IntProperty,
9 BoolProperty,
10 StringProperty,
12 from bpy_extras import object_utils
15 # Create a new mesh (object) from verts/edges/faces
16 # verts/edges/faces ... List of vertices/edges/faces for the
17 # new mesh (as used in from_pydata)
18 # name ... Name of the new mesh (& object)
20 def create_mesh_object(context, verts, edges, faces, name):
22 # Create new mesh
23 mesh = bpy.data.meshes.new(name)
25 # Make a mesh from a list of verts/edges/faces
26 mesh.from_pydata(verts, edges, faces)
28 # Update mesh geometry after adding stuff
29 mesh.update()
31 from bpy_extras import object_utils
32 return object_utils.object_data_add(context, mesh, operator=None)
35 # A very simple "bridge" tool
37 def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
38 faces = []
40 if not vertIdx1 or not vertIdx2:
41 return None
43 if len(vertIdx1) < 2 and len(vertIdx2) < 2:
44 return None
46 fan = False
47 if (len(vertIdx1) != len(vertIdx2)):
48 if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
49 fan = True
50 else:
51 return None
53 total = len(vertIdx2)
55 if closed:
56 # Bridge the start with the end
57 if flipped:
58 face = [
59 vertIdx1[0],
60 vertIdx2[0],
61 vertIdx2[total - 1]]
62 if not fan:
63 face.append(vertIdx1[total - 1])
64 faces.append(face)
66 else:
67 face = [vertIdx2[0], vertIdx1[0]]
68 if not fan:
69 face.append(vertIdx1[total - 1])
70 face.append(vertIdx2[total - 1])
71 faces.append(face)
73 # Bridge the rest of the faces
74 for num in range(total - 1):
75 if flipped:
76 if fan:
77 face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
78 else:
79 face = [vertIdx2[num], vertIdx1[num],
80 vertIdx1[num + 1], vertIdx2[num + 1]]
81 faces.append(face)
82 else:
83 if fan:
84 face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
85 else:
86 face = [vertIdx1[num], vertIdx2[num],
87 vertIdx2[num + 1], vertIdx1[num + 1]]
88 faces.append(face)
90 return faces
93 def add_twisted_torus(major_rad, minor_rad, major_seg, minor_seg, twists):
94 PI_2 = pi * 2.0
95 z_axis = (0.0, 0.0, 1.0)
97 verts = []
98 faces = []
100 edgeloop_prev = []
101 for major_index in range(major_seg):
102 quat = Quaternion(z_axis, (major_index / major_seg) * PI_2)
103 rot_twists = PI_2 * major_index / major_seg * twists
105 edgeloop = []
107 # Create section ring
108 for minor_index in range(minor_seg):
109 angle = (PI_2 * minor_index / minor_seg) + rot_twists
111 vec = Vector((
112 major_rad + (cos(angle) * minor_rad),
113 0.0,
114 sin(angle) * minor_rad))
115 vec = quat @ vec
117 edgeloop.append(len(verts))
118 verts.append(vec)
120 # Remember very first edgeloop
121 if major_index == 0:
122 edgeloop_first = edgeloop
124 # Bridge last with current ring
125 if edgeloop_prev:
126 f = createFaces(edgeloop_prev, edgeloop, closed=True)
127 faces.extend(f)
129 edgeloop_prev = edgeloop
131 # Bridge first and last ring
132 f = createFaces(edgeloop_prev, edgeloop_first, closed=True)
133 faces.extend(f)
135 return verts, faces
138 class AddTwistedTorus(bpy.types.Operator, object_utils.AddObjectHelper):
139 bl_idname = "mesh.primitive_twisted_torus_add"
140 bl_label = "Add Twisted Torus"
141 bl_description = "Construct a twisted torus mesh"
142 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
144 TwistedTorus : BoolProperty(name = "TwistedTorus",
145 default = True,
146 description = "TwistedTorus")
147 change : BoolProperty(name = "Change",
148 default = False,
149 description = "change TwistedTorus")
151 major_radius: FloatProperty(
152 name="Major Radius",
153 description="Radius from the origin to the"
154 " center of the cross section",
155 min=0.01,
156 max=100.0,
157 default=1.0
159 minor_radius: FloatProperty(
160 name="Minor Radius",
161 description="Radius of the torus' cross section",
162 min=0.01,
163 max=100.0,
164 default=0.25
166 major_segments: IntProperty(
167 name="Major Segments",
168 description="Number of segments for the main ring of the torus",
169 min=3,
170 max=256,
171 default=48
173 minor_segments: IntProperty(
174 name="Minor Segments",
175 description="Number of segments for the minor ring of the torus",
176 min=3,
177 max=256,
178 default=12
180 twists: IntProperty(
181 name="Twists",
182 description="Number of twists of the torus",
183 min=0,
184 max=256,
185 default=1
187 use_abso: BoolProperty(
188 name="Use Int/Ext Controls",
189 description="Use the Int/Ext controls for torus dimensions",
190 default=False
192 abso_major_rad: FloatProperty(
193 name="Exterior Radius",
194 description="Total Exterior Radius of the torus",
195 min=0.01,
196 max=100.0,
197 default=1.0
199 abso_minor_rad: FloatProperty(
200 name="Inside Radius",
201 description="Total Interior Radius of the torus",
202 min=0.01,
203 max=100.0,
204 default=0.5
207 def draw(self, context):
208 layout = self.layout
210 layout.prop(self, 'major_radius', expand=True)
211 layout.prop(self, 'minor_radius', expand=True)
212 layout.prop(self, 'major_segments', expand=True)
213 layout.prop(self, 'minor_segments', expand=True)
214 layout.prop(self, 'twists', expand=True)
215 layout.prop(self, 'use_abso', expand=True)
216 layout.prop(self, 'abso_major_rad', expand=True)
217 layout.prop(self, 'abso_minor_rad', expand=True)
219 if self.change == False:
220 col = layout.column(align=True)
221 col.prop(self, 'align', expand=True)
222 col = layout.column(align=True)
223 col.prop(self, 'location', expand=True)
224 col = layout.column(align=True)
225 col.prop(self, 'rotation', expand=True)
227 def execute(self, context):
228 # turn off 'Enter Edit Mode'
229 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
230 bpy.context.preferences.edit.use_enter_edit_mode = False
232 if self.use_abso is True:
233 extra_helper = (self.abso_major_rad - self.abso_minor_rad) * 0.5
234 self.major_radius = self.abso_minor_rad + extra_helper
235 self.minor_radius = extra_helper
237 if bpy.context.mode == "OBJECT":
238 if context.selected_objects != [] and context.active_object and \
239 (context.active_object.data is not None) and ('TwistedTorus' in context.active_object.data.keys()) and \
240 (self.change == True):
241 obj = context.active_object
242 oldmesh = obj.data
243 oldmeshname = obj.data.name
244 verts, faces = add_twisted_torus(
245 self.major_radius,
246 self.minor_radius,
247 self.major_segments,
248 self.minor_segments,
249 self.twists
251 mesh = bpy.data.meshes.new('TwistedTorus')
252 mesh.from_pydata(verts, [], faces)
253 obj.data = mesh
254 for material in oldmesh.materials:
255 obj.data.materials.append(material)
256 bpy.data.meshes.remove(oldmesh)
257 obj.data.name = oldmeshname
258 else:
259 verts, faces = add_twisted_torus(
260 self.major_radius,
261 self.minor_radius,
262 self.major_segments,
263 self.minor_segments,
264 self.twists
266 mesh = bpy.data.meshes.new('TwistedTorus')
267 mesh.from_pydata(verts, [], faces)
268 obj = object_utils.object_data_add(context, mesh, operator=self)
270 obj.data["TwistedTorus"] = True
271 obj.data["change"] = False
272 for prm in TwistedTorusParameters():
273 obj.data[prm] = getattr(self, prm)
275 if bpy.context.mode == "EDIT_MESH":
276 active_object = context.active_object
277 name_active_object = active_object.name
278 bpy.ops.object.mode_set(mode='OBJECT')
279 verts, faces = add_twisted_torus(
280 self.major_radius,
281 self.minor_radius,
282 self.major_segments,
283 self.minor_segments,
284 self.twists
286 mesh = bpy.data.meshes.new('TwistedTorus')
287 mesh.from_pydata(verts, [], faces)
288 obj = object_utils.object_data_add(context, mesh, operator=self)
289 obj.select_set(True)
290 active_object.select_set(True)
291 bpy.context.view_layer.objects.active = active_object
292 bpy.ops.object.join()
293 context.active_object.name = name_active_object
294 bpy.ops.object.mode_set(mode='EDIT')
296 if use_enter_edit_mode:
297 bpy.ops.object.mode_set(mode = 'EDIT')
299 # restore pre operator state
300 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
302 return {'FINISHED'}
304 def TwistedTorusParameters():
305 TwistedTorusParameters = [
306 "major_radius",
307 "minor_radius",
308 "major_segments",
309 "minor_segments",
310 "twists",
311 "use_abso",
312 "abso_major_rad",
313 "abso_minor_rad",
315 return TwistedTorusParameters