1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
8 from mathutils
import Quaternion
, Vector
9 from math
import cos
, sin
, pi
10 from bpy
.props
import (
16 from bpy_extras
import object_utils
19 # Create a new mesh (object) from verts/edges/faces
20 # verts/edges/faces ... List of vertices/edges/faces for the
21 # new mesh (as used in from_pydata)
22 # name ... Name of the new mesh (& object)
24 def create_mesh_object(context
, verts
, edges
, faces
, name
):
27 mesh
= bpy
.data
.meshes
.new(name
)
29 # Make a mesh from a list of verts/edges/faces
30 mesh
.from_pydata(verts
, edges
, faces
)
32 # Update mesh geometry after adding stuff
35 from bpy_extras
import object_utils
36 return object_utils
.object_data_add(context
, mesh
, operator
=None)
39 # A very simple "bridge" tool
41 def createFaces(vertIdx1
, vertIdx2
, closed
=False, flipped
=False):
44 if not vertIdx1
or not vertIdx2
:
47 if len(vertIdx1
) < 2 and len(vertIdx2
) < 2:
51 if (len(vertIdx1
) != len(vertIdx2
)):
52 if (len(vertIdx1
) == 1 and len(vertIdx2
) > 1):
60 # Bridge the start with the end
67 face
.append(vertIdx1
[total
- 1])
71 face
= [vertIdx2
[0], vertIdx1
[0]]
73 face
.append(vertIdx1
[total
- 1])
74 face
.append(vertIdx2
[total
- 1])
77 # Bridge the rest of the faces
78 for num
in range(total
- 1):
81 face
= [vertIdx2
[num
], vertIdx1
[0], vertIdx2
[num
+ 1]]
83 face
= [vertIdx2
[num
], vertIdx1
[num
],
84 vertIdx1
[num
+ 1], vertIdx2
[num
+ 1]]
88 face
= [vertIdx1
[0], vertIdx2
[num
], vertIdx2
[num
+ 1]]
90 face
= [vertIdx1
[num
], vertIdx2
[num
],
91 vertIdx2
[num
+ 1], vertIdx1
[num
+ 1]]
97 def add_twisted_torus(major_rad
, minor_rad
, major_seg
, minor_seg
, twists
):
99 z_axis
= (0.0, 0.0, 1.0)
105 for major_index
in range(major_seg
):
106 quat
= Quaternion(z_axis
, (major_index
/ major_seg
) * PI_2
)
107 rot_twists
= PI_2
* major_index
/ major_seg
* twists
111 # Create section ring
112 for minor_index
in range(minor_seg
):
113 angle
= (PI_2
* minor_index
/ minor_seg
) + rot_twists
116 major_rad
+ (cos(angle
) * minor_rad
),
118 sin(angle
) * minor_rad
))
121 edgeloop
.append(len(verts
))
124 # Remember very first edgeloop
126 edgeloop_first
= edgeloop
128 # Bridge last with current ring
130 f
= createFaces(edgeloop_prev
, edgeloop
, closed
=True)
133 edgeloop_prev
= edgeloop
135 # Bridge first and last ring
136 f
= createFaces(edgeloop_prev
, edgeloop_first
, closed
=True)
142 class AddTwistedTorus(bpy
.types
.Operator
, object_utils
.AddObjectHelper
):
143 bl_idname
= "mesh.primitive_twisted_torus_add"
144 bl_label
= "Add Twisted Torus"
145 bl_description
= "Construct a twisted torus mesh"
146 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
148 TwistedTorus
: BoolProperty(name
= "TwistedTorus",
150 description
= "TwistedTorus")
151 change
: BoolProperty(name
= "Change",
153 description
= "change TwistedTorus")
155 major_radius
: FloatProperty(
157 description
="Radius from the origin to the"
158 " center of the cross section",
163 minor_radius
: FloatProperty(
165 description
="Radius of the torus' cross section",
170 major_segments
: IntProperty(
171 name
="Major Segments",
172 description
="Number of segments for the main ring of the torus",
177 minor_segments
: IntProperty(
178 name
="Minor Segments",
179 description
="Number of segments for the minor ring of the torus",
186 description
="Number of twists of the torus",
191 use_abso
: BoolProperty(
192 name
="Use Int/Ext Controls",
193 description
="Use the Int/Ext controls for torus dimensions",
196 abso_major_rad
: FloatProperty(
197 name
="Exterior Radius",
198 description
="Total Exterior Radius of the torus",
203 abso_minor_rad
: FloatProperty(
204 name
="Inside Radius",
205 description
="Total Interior Radius of the torus",
211 def draw(self
, context
):
214 layout
.prop(self
, 'major_radius', expand
=True)
215 layout
.prop(self
, 'minor_radius', expand
=True)
216 layout
.prop(self
, 'major_segments', expand
=True)
217 layout
.prop(self
, 'minor_segments', expand
=True)
218 layout
.prop(self
, 'twists', expand
=True)
219 layout
.prop(self
, 'use_abso', expand
=True)
220 layout
.prop(self
, 'abso_major_rad', expand
=True)
221 layout
.prop(self
, 'abso_minor_rad', expand
=True)
223 if self
.change
== False:
224 col
= layout
.column(align
=True)
225 col
.prop(self
, 'align', expand
=True)
226 col
= layout
.column(align
=True)
227 col
.prop(self
, 'location', expand
=True)
228 col
= layout
.column(align
=True)
229 col
.prop(self
, 'rotation', expand
=True)
231 def execute(self
, context
):
232 # turn off 'Enter Edit Mode'
233 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
234 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
236 if self
.use_abso
is True:
237 extra_helper
= (self
.abso_major_rad
- self
.abso_minor_rad
) * 0.5
238 self
.major_radius
= self
.abso_minor_rad
+ extra_helper
239 self
.minor_radius
= extra_helper
241 if bpy
.context
.mode
== "OBJECT":
242 if context
.selected_objects
!= [] and context
.active_object
and \
243 (context
.active_object
.data
is not None) and ('TwistedTorus' in context
.active_object
.data
.keys()) and \
244 (self
.change
== True):
245 obj
= context
.active_object
247 oldmeshname
= obj
.data
.name
248 verts
, faces
= add_twisted_torus(
255 mesh
= bpy
.data
.meshes
.new('TwistedTorus')
256 mesh
.from_pydata(verts
, [], faces
)
258 for material
in oldmesh
.materials
:
259 obj
.data
.materials
.append(material
)
260 bpy
.data
.meshes
.remove(oldmesh
)
261 obj
.data
.name
= oldmeshname
263 verts
, faces
= add_twisted_torus(
270 mesh
= bpy
.data
.meshes
.new('TwistedTorus')
271 mesh
.from_pydata(verts
, [], faces
)
272 obj
= object_utils
.object_data_add(context
, mesh
, operator
=self
)
274 obj
.data
["TwistedTorus"] = True
275 obj
.data
["change"] = False
276 for prm
in TwistedTorusParameters():
277 obj
.data
[prm
] = getattr(self
, prm
)
279 if bpy
.context
.mode
== "EDIT_MESH":
280 active_object
= context
.active_object
281 name_active_object
= active_object
.name
282 bpy
.ops
.object.mode_set(mode
='OBJECT')
283 verts
, faces
= add_twisted_torus(
290 mesh
= bpy
.data
.meshes
.new('TwistedTorus')
291 mesh
.from_pydata(verts
, [], faces
)
292 obj
= object_utils
.object_data_add(context
, mesh
, operator
=self
)
294 active_object
.select_set(True)
295 bpy
.context
.view_layer
.objects
.active
= active_object
296 bpy
.ops
.object.join()
297 context
.active_object
.name
= name_active_object
298 bpy
.ops
.object.mode_set(mode
='EDIT')
300 if use_enter_edit_mode
:
301 bpy
.ops
.object.mode_set(mode
= 'EDIT')
303 # restore pre operator state
304 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
308 def TwistedTorusParameters():
309 TwistedTorusParameters
= [
319 return TwistedTorusParameters