1 # GPL # "author": "DreamPainter"
4 from bpy
.props
import (
10 from math
import pi
, cos
, sin
11 from mathutils
import Vector
12 from bpy_extras
import object_utils
15 # A very simple "bridge" tool
17 def createFaces(vertIdx1
, vertIdx2
, closed
=False, flipped
=False):
20 if not vertIdx1
or not vertIdx2
:
23 if len(vertIdx1
) < 2 and len(vertIdx2
) < 2:
27 if (len(vertIdx1
) != len(vertIdx2
)):
28 if (len(vertIdx1
) == 1 and len(vertIdx2
) > 1):
36 # Bridge the start with the end.
43 face
.append(vertIdx1
[total
- 1])
47 face
= [vertIdx2
[0], vertIdx1
[0]]
49 face
.append(vertIdx1
[total
- 1])
50 face
.append(vertIdx2
[total
- 1])
53 # Bridge the rest of the faces.
54 for num
in range(total
- 1):
57 face
= [vertIdx2
[num
], vertIdx1
[0], vertIdx2
[num
+ 1]]
59 face
= [vertIdx2
[num
], vertIdx1
[num
],
60 vertIdx1
[num
+ 1], vertIdx2
[num
+ 1]]
64 face
= [vertIdx1
[0], vertIdx2
[num
], vertIdx2
[num
+ 1]]
66 face
= [vertIdx1
[num
], vertIdx2
[num
],
67 vertIdx2
[num
+ 1], vertIdx1
[num
+ 1]]
79 def supertoroid(R
, r
, u
, v
, n1
, n2
):
83 u = lateral segmentation
84 v = radial segmentation
85 n1 = value determines the shape of the torus
86 n2 = value determines the shape of the cross-section
88 # create the necessary constants
95 # create each cross-section by calculating each vector on the
97 # x = (cos(theta) ** n1)*(R + r * (cos(phi) ** n2))
98 # y = (sin(theta) ** n1)*(R + r * (cos(phi) ** n2))
99 # z = (r * sin(phi) ** n2)
100 # with theta and phi rangeing from 0 to 2pi
103 s
= power(sin(i
* a
), n1
)
104 c
= power(cos(i
* a
), n1
)
106 c2
= R
+ r
* power(cos(j
* b
), n2
)
107 s2
= r
* power(sin(j
* b
), n2
)
108 verts
.append(Vector((c
* c2
, s
* c2
, s2
)))
110 # bridge the last circle with the previous circle
111 if i
> 0: # but not for the first circle, 'cus there's no previous before the first
112 f
= createFaces(range((i
- 1) * v
, i
* v
), range(i
* v
, (i
+ 1) * v
), closed
=True)
114 # bridge the last circle with the first
115 f
= createFaces(range((u
- 1) * v
, u
* v
), range(v
), closed
=True)
121 class add_supertoroid(bpy
.types
.Operator
, object_utils
.AddObjectHelper
):
122 bl_idname
= "mesh.primitive_supertoroid_add"
123 bl_label
= "Add SuperToroid"
124 bl_description
= "Construct a supertoroid mesh"
125 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
127 SuperToroid
: BoolProperty(name
= "SuperToroid",
129 description
= "SuperToroid")
130 change
: BoolProperty(name
= "Change",
132 description
= "change SuperToroid")
136 description
="The radius inside the tube",
142 description
="The radius of the tube",
148 description
="Radial segmentation",
154 description
="Lateral segmentation",
159 name
="Ring manipulator",
160 description
="Manipulates the shape of the Ring",
165 name
="Cross manipulator",
166 description
="Manipulates the shape of the cross-section",
171 name
="Use Int. and Ext. radii",
172 description
="Use internal and external radii",
182 def draw(self
, context
):
185 layout
.prop(self
, 'R', expand
=True)
186 layout
.prop(self
, 'r', expand
=True)
187 layout
.prop(self
, 'u', expand
=True)
188 layout
.prop(self
, 'v', expand
=True)
189 layout
.prop(self
, 'n1', expand
=True)
190 layout
.prop(self
, 'n2', expand
=True)
191 layout
.prop(self
, 'ie', expand
=True)
193 if self
.change
== False:
194 col
= layout
.column(align
=True)
195 col
.prop(self
, 'align', expand
=True)
196 col
= layout
.column(align
=True)
197 col
.prop(self
, 'location', expand
=True)
198 col
= layout
.column(align
=True)
199 col
.prop(self
, 'rotation', expand
=True)
201 def execute(self
, context
):
202 # turn off 'Enter Edit Mode'
203 use_enter_edit_mode
= bpy
.context
.preferences
.edit
.use_enter_edit_mode
204 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= False
206 props
= self
.properties
208 # check how the radii properties must be used
210 rad1
= (props
.R
+ props
.r
) / 2
211 rad2
= (props
.R
- props
.r
) / 2
212 # for consistency in the mesh, ie no crossing faces, make the largest of the two
215 [rad1
, rad2
] = [rad2
, rad1
]
219 # again for consistency, make the radius in the tube,
220 # at least as big as the radius of the tube
224 if bpy
.context
.mode
== "OBJECT":
225 if context
.selected_objects
!= [] and context
.active_object
and \
226 (context
.active_object
.data
is not None) and ('SuperToroid' in context
.active_object
.data
.keys()) and \
227 (self
.change
== True):
228 obj
= context
.active_object
230 oldmeshname
= obj
.data
.name
231 verts
, faces
= supertoroid(rad1
,
238 mesh
= bpy
.data
.meshes
.new('SuperToroid')
239 mesh
.from_pydata(verts
, [], faces
)
241 for material
in oldmesh
.materials
:
242 obj
.data
.materials
.append(material
)
243 bpy
.data
.meshes
.remove(oldmesh
)
244 obj
.data
.name
= oldmeshname
246 verts
, faces
= supertoroid(rad1
,
253 mesh
= bpy
.data
.meshes
.new('SuperToroid')
254 mesh
.from_pydata(verts
, [], faces
)
255 obj
= object_utils
.object_data_add(context
, mesh
, operator
=self
)
257 obj
.data
["SuperToroid"] = True
258 obj
.data
["change"] = False
259 for prm
in SuperToroidParameters():
260 obj
.data
[prm
] = getattr(self
, prm
)
262 if bpy
.context
.mode
== "EDIT_MESH":
263 active_object
= context
.active_object
264 name_active_object
= active_object
.name
265 bpy
.ops
.object.mode_set(mode
='OBJECT')
266 verts
, faces
= supertoroid(rad1
,
273 mesh
= bpy
.data
.meshes
.new('SuperToroid')
274 mesh
.from_pydata(verts
, [], faces
)
275 obj
= object_utils
.object_data_add(context
, mesh
, operator
=self
)
277 active_object
.select_set(True)
278 bpy
.context
.view_layer
.objects
.active
= active_object
279 bpy
.ops
.object.join()
280 context
.active_object
.name
= name_active_object
281 bpy
.ops
.object.mode_set(mode
='EDIT')
283 if use_enter_edit_mode
:
284 bpy
.ops
.object.mode_set(mode
= 'EDIT')
286 # restore pre operator state
287 bpy
.context
.preferences
.edit
.use_enter_edit_mode
= use_enter_edit_mode
291 def SuperToroidParameters():
292 SuperToroidParameters
= [
302 return SuperToroidParameters