Remove workaround for uuid, resolved with the Python3.4x and MSVC2013 move.
[blender-addons.git] / add_mesh_pipe_joint.py
blobadb9aea12730bfe73a6ba70bf8189f7721e3d4cf
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Pipe Joints",
21 "author": "Buerbaum Martin (Pontiac)",
22 "version": (0, 10, 7),
23 "blender": (2, 61, 0),
24 "location": "View3D > Add > Mesh > Pipe Joints",
25 "description": "Add different types of pipe joints",
26 "warning": "",
27 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Add_Mesh/Add_Pipe_Joints",
29 "tracker_url": "https://developer.blender.org/T21443",
30 "category": "Add Mesh"}
32 import bpy
33 from math import *
34 from bpy.props import *
37 # Create a new mesh (object) from verts/edges/faces.
38 # verts/edges/faces ... List of vertices/edges/faces for the
39 # new mesh (as used in from_pydata).
40 # name ... Name of the new mesh (& object).
41 def create_mesh_object(context, verts, edges, faces, name):
42 # Create new mesh
43 mesh = bpy.data.meshes.new(name)
45 # Make a mesh from a list of verts/edges/faces.
46 mesh.from_pydata(verts, edges, faces)
48 # Update mesh geometry after adding stuff.
49 mesh.update()
51 from bpy_extras import object_utils
52 return object_utils.object_data_add(context, mesh, operator=None)
54 # A very simple "bridge" tool.
55 # Connects two equally long vertex rows with faces.
56 # Returns a list of the new faces (list of lists)
58 # vertIdx1 ... First vertex list (list of vertex indices).
59 # vertIdx2 ... Second vertex list (list of vertex indices).
60 # closed ... Creates a loop (first & last are closed).
61 # flipped ... Invert the normal of the face(s).
63 # Note: You can set vertIdx1 to a single vertex index to create
64 # a fan/star of faces.
65 # Note: If both vertex idx list are the same length they have
66 # to have at least 2 vertices.
67 def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
68 faces = []
70 if not vertIdx1 or not vertIdx2:
71 return None
73 if len(vertIdx1) < 2 and len(vertIdx2) < 2:
74 return None
76 fan = False
77 if (len(vertIdx1) != len(vertIdx2)):
78 if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
79 fan = True
80 else:
81 return None
83 total = len(vertIdx2)
85 if closed:
86 # Bridge the start with the end.
87 if flipped:
88 face = [
89 vertIdx1[0],
90 vertIdx2[0],
91 vertIdx2[total - 1]]
92 if not fan:
93 face.append(vertIdx1[total - 1])
94 faces.append(face)
96 else:
97 face = [vertIdx2[0], vertIdx1[0]]
98 if not fan:
99 face.append(vertIdx1[total - 1])
100 face.append(vertIdx2[total - 1])
101 faces.append(face)
103 # Bridge the rest of the faces.
104 for num in range(total - 1):
105 if flipped:
106 if fan:
107 face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
108 else:
109 face = [vertIdx2[num], vertIdx1[num],
110 vertIdx1[num + 1], vertIdx2[num + 1]]
111 faces.append(face)
112 else:
113 if fan:
114 face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
115 else:
116 face = [vertIdx1[num], vertIdx2[num],
117 vertIdx2[num + 1], vertIdx1[num + 1]]
118 faces.append(face)
120 return faces
123 class AddElbowJoint(bpy.types.Operator):
124 # Create the vertices and polygons for a simple elbow (bent pipe).
125 """Add an Elbow pipe mesh"""
126 bl_idname = "mesh.primitive_elbow_joint_add"
127 bl_label = "Add Pipe Elbow"
128 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
130 radius = FloatProperty(name="Radius",
131 description="The radius of the pipe",
132 default=1.0,
133 min=0.01,
134 max=100.0,
135 unit="LENGTH")
136 div = IntProperty(name="Divisions",
137 description="Number of vertices (divisions)",
138 default=32, min=3, max=256)
140 angle = FloatProperty(name="Angle",
141 description="The angle of the branching pipe (i.e. the 'arm' - " \
142 "Measured from the center line of the main pipe",
143 default=radians(45.0),
144 min=radians(-179.9),
145 max=radians(179.9),
146 unit="ROTATION")
148 startLength = FloatProperty(name="Length Start",
149 description="Length of the beginning of the pipe",
150 default=3.0,
151 min=0.01,
152 max=100.0,
153 unit="LENGTH")
154 endLength = FloatProperty(name="End Length",
155 description="Length of the end of the pipe",
156 default=3.0,
157 min=0.01,
158 max=100.0,
159 unit="LENGTH")
161 def execute(self, context):
163 radius = self.radius
164 div = self.div
166 angle = self.angle
168 startLength = self.startLength
169 endLength = self.endLength
171 verts = []
172 faces = []
174 loop1 = [] # The starting circle
175 loop2 = [] # The elbow circle
176 loop3 = [] # The end circle
178 # Create start circle
179 for vertIdx in range(div):
180 curVertAngle = vertIdx * (2.0 * pi / div)
181 locX = sin(curVertAngle)
182 locY = cos(curVertAngle)
183 locZ = -startLength
184 loop1.append(len(verts))
185 verts.append([locX * radius, locY * radius, locZ])
187 # Create deformed joint circle
188 for vertIdx in range(div):
189 curVertAngle = vertIdx * (2.0 * pi / div)
190 locX = sin(curVertAngle)
191 locY = cos(curVertAngle)
192 locZ = locX * tan(angle / 2.0)
193 loop2.append(len(verts))
194 verts.append([locX * radius, locY * radius, locZ * radius])
196 # Create end circle
197 baseEndLocX = -endLength * sin(angle)
198 baseEndLocZ = endLength * cos(angle)
199 for vertIdx in range(div):
200 curVertAngle = vertIdx * (2.0 * pi / div)
201 # Create circle
202 locX = sin(curVertAngle) * radius
203 locY = cos(curVertAngle) * radius
204 locZ = 0.0
206 # Rotate circle
207 locZ = locX * cos(pi / 2.0 - angle)
208 locX = locX * sin(pi / 2.0 - angle)
210 loop3.append(len(verts))
211 # Translate and add circle vertices to the list.
212 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
214 # Create faces
215 faces.extend(createFaces(loop1, loop2, closed=True))
216 faces.extend(createFaces(loop2, loop3, closed=True))
218 base = create_mesh_object(context, verts, [], faces, "Elbow Joint")
220 return {'FINISHED'}
223 class AddTeeJoint(bpy.types.Operator):
224 # Create the vertices and polygons for a simple tee (T) joint.
225 # The base arm of the T can be positioned in an angle if needed though.
226 """Add a Tee-Joint mesh"""
227 bl_idname = "mesh.primitive_tee_joint_add"
228 bl_label = "Add Pipe Tee-Joint"
229 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
231 radius = FloatProperty(name="Radius",
232 description="The radius of the pipe",
233 default=1.0,
234 min=0.01,
235 max=100.0,
236 unit="LENGTH")
237 div = IntProperty(name="Divisions",
238 description="Number of vertices (divisions)",
239 default=32,
240 min=4,
241 max=256)
243 angle = FloatProperty(name="Angle",
244 description="The angle of the branching pipe (i.e. the 'arm' - " \
245 "Measured from the center line of the main pipe",
246 default=radians(90.0),
247 min=radians(0.1),
248 max=radians(179.9),
249 unit="ROTATION")
251 startLength = FloatProperty(name="Length Start",
252 description="Length of the beginning of the" \
253 " main pipe (the straight one)",
254 default=3.0,
255 min=0.01,
256 max=100.0,
257 unit="LENGTH")
258 endLength = FloatProperty(name="End Length",
259 description="Length of the end of the" \
260 " main pipe (the straight one)",
261 default=3.0,
262 min=0.01,
263 max=100.0,
264 unit="LENGTH")
265 branchLength = FloatProperty(name="Arm Length",
266 description="Length of the arm pipe (the bent one)",
267 default=3.0,
268 min=0.01,
269 max=100.0,
270 unit="LENGTH")
272 def execute(self, context):
274 radius = self.radius
275 div = self.div
277 angle = self.angle
279 startLength = self.startLength
280 endLength = self.endLength
281 branchLength = self.branchLength
283 if (div % 2):
284 # Odd vertice number not supported (yet).
285 return {'CANCELLED'}
287 verts = []
288 faces = []
290 # List of vert indices of each cross section
291 loopMainStart = [] # Vert indices for the
292 # beginning of the main pipe.
293 loopJoint1 = [] # Vert indices for joint that is used
294 # to connect the joint & loopMainStart.
295 loopJoint2 = [] # Vert indices for joint that is used
296 # to connect the joint & loopArm.
297 loopJoint3 = [] # Vert index for joint that is used
298 # to connect the joint & loopMainEnd.
299 loopArm = [] # Vert indices for the end of the arm.
300 loopMainEnd = [] # Vert indices for the
301 # end of the main pipe.
303 # Create start circle (main pipe)
304 for vertIdx in range(div):
305 curVertAngle = vertIdx * (2.0 * pi / div)
306 locX = sin(curVertAngle)
307 locY = cos(curVertAngle)
308 locZ = -startLength
309 loopMainStart.append(len(verts))
310 verts.append([locX * radius, locY * radius, locZ])
312 # Create deformed joint circle
313 vertTemp1 = None
314 vertTemp2 = None
315 for vertIdx in range(div):
316 curVertAngle = vertIdx * (2.0 * pi / div)
317 locX = sin(curVertAngle)
318 locY = cos(curVertAngle)
320 if vertIdx == 0:
321 vertTemp1 = len(verts)
322 if vertIdx == div / 2:
323 # @todo: This will possibly break if we
324 # ever support odd divisions.
325 vertTemp2 = len(verts)
327 loopJoint1.append(len(verts))
328 if (vertIdx < div / 2):
329 # Straight side of main pipe.
330 locZ = 0
331 loopJoint3.append(len(verts))
332 else:
333 # Branching side
334 locZ = locX * tan(angle / 2.0)
335 loopJoint2.append(len(verts))
337 verts.append([locX * radius, locY * radius, locZ * radius])
339 # Create 2. deformed joint (half-)circle
340 loopTemp = []
341 for vertIdx in range(div):
342 if (vertIdx > div / 2):
343 curVertAngle = vertIdx * (2.0 * pi / div)
344 locX = sin(curVertAngle)
345 locY = -cos(curVertAngle)
346 locZ = -(radius * locX * tan((pi - angle) / 2.0))
347 loopTemp.append(len(verts))
348 verts.append([locX * radius, locY * radius, locZ])
350 loopTemp2 = loopTemp[:]
352 # Finalise 2. loop
353 loopTemp.reverse()
354 loopTemp.append(vertTemp1)
355 loopJoint2.reverse()
356 loopJoint2.extend(loopTemp)
357 loopJoint2.reverse()
359 # Finalise 3. loop
360 loopTemp2.append(vertTemp2)
361 loopTemp2.reverse()
362 loopJoint3.extend(loopTemp2)
364 # Create end circle (branching pipe)
365 baseEndLocX = -branchLength * sin(angle)
366 baseEndLocZ = branchLength * cos(angle)
367 for vertIdx in range(div):
368 curVertAngle = vertIdx * (2.0 * pi / div)
369 # Create circle
370 locX = sin(curVertAngle) * radius
371 locY = cos(curVertAngle) * radius
372 locZ = 0.0
374 # Rotate circle
375 locZ = locX * cos(pi / 2.0 - angle)
376 locX = locX * sin(pi / 2.0 - angle)
378 loopArm.append(len(verts))
380 # Add translated circle.
381 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
383 # Create end circle (main pipe)
384 for vertIdx in range(div):
385 curVertAngle = vertIdx * (2.0 * pi / div)
386 locX = sin(curVertAngle)
387 locY = cos(curVertAngle)
388 locZ = endLength
389 loopMainEnd.append(len(verts))
390 verts.append([locX * radius, locY * radius, locZ])
392 # Create faces
393 faces.extend(createFaces(loopMainStart, loopJoint1, closed=True))
394 faces.extend(createFaces(loopJoint2, loopArm, closed=True))
395 faces.extend(createFaces(loopJoint3, loopMainEnd, closed=True))
397 base = create_mesh_object(context, verts, [], faces, "Tee Joint")
399 return {'FINISHED'}
402 class AddWyeJoint(bpy.types.Operator):
403 """Add a Wye-Joint mesh"""
404 bl_idname = "mesh.primitive_wye_joint_add"
405 bl_label = "Add Pipe Wye-Joint"
406 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
408 radius = FloatProperty(name="Radius",
409 description="The radius of the pipe",
410 default=1.0,
411 min=0.01,
412 max=100.0,
413 unit="LENGTH")
414 div = IntProperty(name="Divisions",
415 description="Number of vertices (divisions)",
416 default=32,
417 min=4,
418 max=256)
420 angle1 = FloatProperty(name="Angle 1",
421 description="The angle of the 1. branching pipe " \
422 "(measured from the center line of the main pipe)",
423 default=radians(45.0),
424 min=radians(-179.9),
425 max=radians(179.9),
426 unit="ROTATION")
427 angle2 = FloatProperty(name="Angle 2",
428 description="The angle of the 2. branching pipe " \
429 "(measured from the center line of the main pipe) ",
430 default=radians(45.0),
431 min=radians(-179.9),
432 max=radians(179.9),
433 unit="ROTATION")
435 startLength = FloatProperty(name="Length Start",
436 description="Length of the beginning of the" \
437 " main pipe (the straight one)",
438 default=3.0,
439 min=0.01,
440 max=100.0,
441 unit="LENGTH")
442 branch1Length = FloatProperty(name="Length Arm 1",
443 description="Length of the 1. arm",
444 default=3.0,
445 min=0.01,
446 max=100.0,
447 unit="LENGTH")
448 branch2Length = FloatProperty(name="Length Arm 2",
449 description="Length of the 2. arm",
450 default=3.0,
451 min=0.01,
452 max=100.0,
453 unit="LENGTH")
455 def execute(self, context):
457 radius = self.radius
458 div = self.div
460 angle1 = self.angle1
461 angle2 = self.angle2
463 startLength = self.startLength
464 branch1Length = self.branch1Length
465 branch2Length = self.branch2Length
467 if (div % 2):
468 # Odd vertice number not supported (yet).
469 return {'CANCELLED'}
471 verts = []
472 faces = []
474 # List of vert indices of each cross section
475 loopMainStart = [] # Vert indices for
476 # the beginning of the main pipe.
477 loopJoint1 = [] # Vert index for joint that is used
478 # to connect the joint & loopMainStart.
479 loopJoint2 = [] # Vert index for joint that
480 # is used to connect the joint & loopArm1.
481 loopJoint3 = [] # Vert index for joint that is
482 # used to connect the joint & loopArm2.
483 loopArm1 = [] # Vert idxs for end of the 1. arm.
484 loopArm2 = [] # Vert idxs for end of the 2. arm.
486 # Create start circle
487 for vertIdx in range(div):
488 curVertAngle = vertIdx * (2.0 * pi / div)
489 locX = sin(curVertAngle)
490 locY = cos(curVertAngle)
491 locZ = -startLength
492 loopMainStart.append(len(verts))
493 verts.append([locX * radius, locY * radius, locZ])
495 # Create deformed joint circle
496 vertTemp1 = None
497 vertTemp2 = None
498 for vertIdx in range(div):
499 curVertAngle = vertIdx * (2.0 * pi / div)
500 locX = sin(curVertAngle)
501 locY = cos(curVertAngle)
503 if vertIdx == 0:
504 vertTemp2 = len(verts)
505 if vertIdx == div / 2:
506 # @todo: This will possibly break if we
507 # ever support odd divisions.
508 vertTemp1 = len(verts)
510 loopJoint1.append(len(verts))
511 if (vertIdx > div / 2):
512 locZ = locX * tan(angle1 / 2.0)
513 loopJoint2.append(len(verts))
514 else:
515 locZ = locX * tan(-angle2 / 2.0)
516 loopJoint3.append(len(verts))
518 verts.append([locX * radius, locY * radius, locZ * radius])
520 # Create 2. deformed joint (half-)circle
521 loopTemp = []
522 angleJoint = (angle2 - angle1) / 2.0
523 for vertIdx in range(div):
524 if (vertIdx > div / 2):
525 curVertAngle = vertIdx * (2.0 * pi / div)
527 locX = (-sin(curVertAngle) * sin(angleJoint)
528 / sin(angle2 - angleJoint))
529 locY = -cos(curVertAngle)
530 locZ = (-(sin(curVertAngle) * cos(angleJoint)
531 / sin(angle2 - angleJoint)))
533 loopTemp.append(len(verts))
534 verts.append([locX * radius, locY * radius, locZ * radius])
536 loopTemp2 = loopTemp[:]
538 # Finalise 2. loop
539 loopTemp.append(vertTemp1)
540 loopTemp.reverse()
541 loopTemp.append(vertTemp2)
542 loopJoint2.reverse()
543 loopJoint2.extend(loopTemp)
544 loopJoint2.reverse()
546 # Finalise 3. loop
547 loopTemp2.reverse()
548 loopJoint3.extend(loopTemp2)
550 # Create end circle (1. branching pipe)
551 baseEndLocX = -branch1Length * sin(angle1)
552 baseEndLocZ = branch1Length * cos(angle1)
553 for vertIdx in range(div):
554 curVertAngle = vertIdx * (2.0 * pi / div)
555 # Create circle
556 locX = sin(curVertAngle) * radius
557 locY = cos(curVertAngle) * radius
558 locZ = 0.0
560 # Rotate circle
561 locZ = locX * cos(pi / 2.0 - angle1)
562 locX = locX * sin(pi / 2.0 - angle1)
564 loopArm1.append(len(verts))
565 # Add translated circle.
566 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
568 # Create end circle (2. branching pipe)
569 baseEndLocX = branch2Length * sin(angle2)
570 baseEndLocZ = branch2Length * cos(angle2)
571 for vertIdx in range(div):
572 curVertAngle = vertIdx * (2.0 * pi / div)
573 # Create circle
574 locX = sin(curVertAngle) * radius
575 locY = cos(curVertAngle) * radius
576 locZ = 0.0
578 # Rotate circle
579 locZ = locX * cos(pi / 2.0 + angle2)
580 locX = locX * sin(pi / 2.0 + angle2)
582 loopArm2.append(len(verts))
583 # Add translated circle
584 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
586 # Create faces
587 faces.extend(createFaces(loopMainStart, loopJoint1, closed=True))
588 faces.extend(createFaces(loopJoint2, loopArm1, closed=True))
589 faces.extend(createFaces(loopJoint3, loopArm2, closed=True))
591 base = create_mesh_object(context, verts, [], faces, "Wye Joint")
593 return {'FINISHED'}
596 class AddCrossJoint(bpy.types.Operator):
597 """Add a Cross-Joint mesh"""
598 # Create the vertices and polygons for a coss (+ or X) pipe joint.
599 bl_idname = "mesh.primitive_cross_joint_add"
600 bl_label = "Add Pipe Cross-Joint"
601 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
603 radius = FloatProperty(name="Radius",
604 description="The radius of the pipe",
605 default=1.0,
606 min=0.01,
607 max=100.0,
608 unit="LENGTH")
609 div = IntProperty(name="Divisions",
610 description="Number of vertices (divisions)",
611 default=32,
612 min=4,
613 max=256)
615 angle1 = FloatProperty(name="Angle 1",
616 description="The angle of the 1. arm (from the main axis)",
617 default=radians(90.0),
618 min=radians(-179.9),
619 max=radians(179.9),
620 unit="ROTATION")
621 angle2 = FloatProperty(name="Angle 2",
622 description="The angle of the 2. arm (from the main axis)",
623 default=radians(90.0),
624 min=radians(-179.9),
625 max=radians(179.9),
626 unit="ROTATION")
627 angle3 = FloatProperty(name="Angle 3 (center)",
628 description="The angle of the center arm (from the main axis)",
629 default=radians(0.0),
630 min=radians(-179.9),
631 max=radians(179.9),
632 unit="ROTATION")
634 startLength = FloatProperty(name="Length Start",
635 description="Length of the beginning of the " \
636 "main pipe (the straight one)",
637 default=3.0,
638 min=0.01,
639 max=100.0,
640 unit="LENGTH")
641 branch1Length = FloatProperty(name="Length Arm 1",
642 description="Length of the 1. arm",
643 default=3.0,
644 min=0.01,
645 max=100.0,
646 unit="LENGTH")
647 branch2Length = FloatProperty(name="Length Arm 2",
648 description="Length of the 2. arm",
649 default=3.0,
650 min=0.01,
651 max=100.0,
652 unit="LENGTH")
653 branch3Length = FloatProperty(name="Length Arm 3 (center)",
654 description="Length of the center arm",
655 default=3.0,
656 min=0.01,
657 max=100.0,
658 unit="LENGTH")
660 def execute(self, context):
662 radius = self.radius
663 div = self.div
665 angle1 = self.angle1
666 angle2 = self.angle2
667 angle3 = self.angle3
669 startLength = self.startLength
670 branch1Length = self.branch1Length
671 branch2Length = self.branch2Length
672 branch3Length = self.branch3Length
673 if (div % 2):
674 # Odd vertice number not supported (yet).
675 return {'CANCELLED'}
677 verts = []
678 faces = []
680 # List of vert indices of each cross section
681 loopMainStart = [] # Vert indices for the
682 # beginning of the main pipe.
683 loopJoint1 = [] # Vert index for joint that is used
684 # to connect the joint & loopMainStart.
685 loopJoint2 = [] # Vert index for joint that is used
686 # to connect the joint & loopArm1.
687 loopJoint3 = [] # Vert index for joint that is used
688 # to connect the joint & loopArm2.
689 loopJoint4 = [] # Vert index for joint that is used
690 # to connect the joint & loopArm3.
691 loopArm1 = [] # Vert idxs for the end of the 1. arm.
692 loopArm2 = [] # Vert idxs for the end of the 2. arm.
693 loopArm3 = [] # Vert idxs for the center arm end.
695 # Create start circle
696 for vertIdx in range(div):
697 curVertAngle = vertIdx * (2.0 * pi / div)
698 locX = sin(curVertAngle)
699 locY = cos(curVertAngle)
700 locZ = -startLength
701 loopMainStart.append(len(verts))
702 verts.append([locX * radius, locY * radius, locZ])
704 # Create 1. deformed joint circle
705 vertTemp1 = None
706 vertTemp2 = None
707 for vertIdx in range(div):
708 curVertAngle = vertIdx * (2.0 * pi / div)
709 locX = sin(curVertAngle)
710 locY = cos(curVertAngle)
712 if vertIdx == 0:
713 vertTemp2 = len(verts)
714 if vertIdx == div / 2:
715 # @todo: This will possibly break if we
716 # ever support odd divisions.
717 vertTemp1 = len(verts)
719 loopJoint1.append(len(verts))
720 if (vertIdx > div / 2):
721 locZ = locX * tan(angle1 / 2.0)
722 loopJoint2.append(len(verts))
723 else:
724 locZ = locX * tan(-angle2 / 2.0)
725 loopJoint3.append(len(verts))
727 verts.append([locX * radius, locY * radius, locZ * radius])
729 # loopTemp2 = loopJoint2[:] # UNUSED
731 # Create 2. deformed joint circle
732 loopTempA = []
733 loopTempB = []
734 angleJoint1 = (angle1 - angle3) / 2.0
735 angleJoint2 = (angle2 + angle3) / 2.0
736 for vertIdx in range(div):
737 curVertAngle = vertIdx * (2.0 * pi / div)
739 # Skip pole vertices
740 # @todo: This will possibly break if
741 # we ever support odd divisions.
742 if not (vertIdx == 0) and not (vertIdx == div / 2):
744 if (vertIdx > div / 2):
745 angleJoint = angleJoint1
746 angle = angle1
747 Z = -1.0
748 loopTempA.append(len(verts))
750 else:
751 angleJoint = angleJoint2
752 angle = angle2
753 Z = 1.0
754 loopTempB.append(len(verts))
756 locX = (sin(curVertAngle) * sin(angleJoint)
757 / sin(angle - angleJoint))
758 locY = -cos(curVertAngle)
759 locZ = (Z * (sin(curVertAngle) * cos(angleJoint)
760 / sin(angle - angleJoint)))
762 verts.append([locX * radius, locY * radius, locZ * radius])
764 loopTempA2 = loopTempA[:]
765 loopTempB2 = loopTempB[:]
766 loopTempB3 = loopTempB[:]
768 # Finalise 2. loop
769 loopTempA.append(vertTemp1)
770 loopTempA.reverse()
771 loopTempA.append(vertTemp2)
772 loopJoint2.reverse()
773 loopJoint2.extend(loopTempA)
774 loopJoint2.reverse()
776 # Finalise 3. loop
777 loopJoint3.extend(loopTempB3)
779 # Finalise 4. loop
780 loopTempA2.append(vertTemp1)
781 loopTempA2.reverse()
782 loopTempB2.append(vertTemp2)
783 loopJoint4.extend(reversed(loopTempB2))
784 loopJoint4.extend(loopTempA2)
786 # Create end circle (1. branching pipe)
787 baseEndLocX = -branch1Length * sin(angle1)
788 baseEndLocZ = branch1Length * cos(angle1)
789 for vertIdx in range(div):
790 curVertAngle = vertIdx * (2.0 * pi / div)
791 # Create circle
792 locX = sin(curVertAngle) * radius
793 locY = cos(curVertAngle) * radius
794 locZ = 0.0
796 # Rotate circle
797 locZ = locX * cos(pi / 2.0 - angle1)
798 locX = locX * sin(pi / 2.0 - angle1)
800 loopArm1.append(len(verts))
801 # Add translated circle.
802 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
804 # Create end circle (2. branching pipe)
805 baseEndLocX = branch2Length * sin(angle2)
806 baseEndLocZ = branch2Length * cos(angle2)
807 for vertIdx in range(div):
808 curVertAngle = vertIdx * (2.0 * pi / div)
809 # Create circle
810 locX = sin(curVertAngle) * radius
811 locY = cos(curVertAngle) * radius
812 locZ = 0.0
814 # Rotate circle
815 locZ = locX * cos(pi / 2.0 + angle2)
816 locX = locX * sin(pi / 2.0 + angle2)
818 loopArm2.append(len(verts))
819 # Add translated circle
820 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
822 # Create end circle (center pipe)
823 baseEndLocX = branch3Length * sin(angle3)
824 baseEndLocZ = branch3Length * cos(angle3)
825 for vertIdx in range(div):
826 curVertAngle = vertIdx * (2.0 * pi / div)
827 # Create circle
828 locX = sin(curVertAngle) * radius
829 locY = cos(curVertAngle) * radius
830 locZ = 0.0
832 # Rotate circle
833 locZ = locX * cos(pi / 2.0 + angle3)
834 locX = locX * sin(pi / 2.0 + angle3)
836 loopArm3.append(len(verts))
837 # Add translated circle
838 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
840 # Create faces
841 faces.extend(createFaces(loopMainStart, loopJoint1, closed=True))
842 faces.extend(createFaces(loopJoint2, loopArm1, closed=True))
843 faces.extend(createFaces(loopJoint3, loopArm2, closed=True))
844 faces.extend(createFaces(loopJoint4, loopArm3, closed=True))
846 base = create_mesh_object(context, verts, [], faces, "Cross Joint")
848 return {'FINISHED'}
851 class AddNJoint(bpy.types.Operator):
852 """Add a N-Joint mesh"""
853 # Create the vertices and polygons for a regular n-joint.
854 bl_idname = "mesh.primitive_n_joint_add"
855 bl_label = "Add Pipe N-Joint"
856 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
858 radius = FloatProperty(name="Radius",
859 description="The radius of the pipe",
860 default=1.0,
861 min=0.01,
862 max=100.0,
863 unit="LENGTH")
864 div = IntProperty(name="Divisions",
865 description="Number of vertices (divisions)",
866 default=32,
867 min=4,
868 max=256)
869 number = IntProperty(name="Arms/Joints",
870 description="Number of joints/arms",
871 default=5,
872 min=2,
873 max=99999)
874 length = FloatProperty(name="Length",
875 description="Length of each joint/arm",
876 default=3.0,
877 min=0.01,
878 max=100.0,
879 unit="LENGTH")
881 def execute(self, context):
882 radius = self.radius
883 div = self.div
884 number = self.number
885 length = self.length
887 if (div % 2):
888 # Odd vertice number not supported (yet).
889 return {'CANCELLED'}
891 if (number < 2):
892 return {'CANCELLED'}
894 verts = []
895 faces = []
897 loopsEndCircles = []
898 loopsJointsTemp = []
899 loopsJoints = []
901 vertTemp1 = None
902 vertTemp2 = None
904 angleDiv = (2.0 * pi / number)
906 # Create vertices for the end circles.
907 for num in range(number):
908 circle = []
909 # Create start circle
910 angle = num * angleDiv
912 baseEndLocX = length * sin(angle)
913 baseEndLocZ = length * cos(angle)
914 for vertIdx in range(div):
915 curVertAngle = vertIdx * (2.0 * pi / div)
916 # Create circle
917 locX = sin(curVertAngle) * radius
918 locY = cos(curVertAngle) * radius
919 locZ = 0.0
921 # Rotate circle
922 locZ = locX * cos(pi / 2.0 + angle)
923 locX = locX * sin(pi / 2.0 + angle)
925 circle.append(len(verts))
926 # Add translated circle
927 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
929 loopsEndCircles.append(circle)
931 # Create vertices for the joint circles.
932 loopJoint = []
933 for vertIdx in range(div):
934 curVertAngle = vertIdx * (2.0 * pi / div)
935 locX = sin(curVertAngle)
936 locY = cos(curVertAngle)
938 skipVert = False
939 # Store pole vertices
940 if vertIdx == 0:
941 if (num == 0):
942 vertTemp2 = len(verts)
943 else:
944 skipVert = True
945 elif vertIdx == div / 2:
946 # @todo: This will possibly break if we
947 # ever support odd divisions.
948 if (num == 0):
949 vertTemp1 = len(verts)
950 else:
951 skipVert = True
953 if not skipVert:
954 if (vertIdx > div / 2):
955 locZ = -locX * tan((pi - angleDiv) / 2.0)
956 loopJoint.append(len(verts))
958 # Rotate the vert
959 cosAng = cos(-angle)
960 sinAng = sin(-angle)
961 LocXnew = locX * cosAng - locZ * sinAng
962 LocZnew = locZ * cosAng + locX * sinAng
963 locZ = LocZnew
964 locX = LocXnew
966 verts.append([
967 locX * radius,
968 locY * radius,
969 locZ * radius])
970 else:
971 # These two vertices will only be
972 # added the very first time.
973 if vertIdx == 0 or vertIdx == div / 2:
974 verts.append([locX * radius, locY * radius, locZ])
976 loopsJointsTemp.append(loopJoint)
978 # Create complete loops (loopsJoints) out of the
979 # double number of half loops in loopsJointsTemp.
980 for halfLoopIdx in range(len(loopsJointsTemp)):
981 if (halfLoopIdx == len(loopsJointsTemp) - 1):
982 idx1 = halfLoopIdx
983 idx2 = 0
984 else:
985 idx1 = halfLoopIdx
986 idx2 = halfLoopIdx + 1
988 loopJoint = []
989 loopJoint.append(vertTemp2)
990 loopJoint.extend(reversed(loopsJointsTemp[idx2]))
991 loopJoint.append(vertTemp1)
992 loopJoint.extend(loopsJointsTemp[idx1])
994 loopsJoints.append(loopJoint)
996 # Create faces from the two
997 # loop arrays (loopsJoints -> loopsEndCircles).
998 for loopIdx in range(len(loopsEndCircles)):
999 faces.extend(
1000 createFaces(loopsJoints[loopIdx],
1001 loopsEndCircles[loopIdx], closed=True))
1003 base = create_mesh_object(context, verts, [], faces, "N Joint")
1005 return {'FINISHED'}
1008 class INFO_MT_mesh_pipe_joints_add(bpy.types.Menu):
1009 # Define the "Pipe Joints" menu
1010 bl_idname = "INFO_MT_mesh_pipe_joints_add"
1011 bl_label = "Pipe Joints"
1013 def draw(self, context):
1014 layout = self.layout
1015 layout.operator_context = 'INVOKE_REGION_WIN'
1016 layout.operator("mesh.primitive_elbow_joint_add",
1017 text="Pipe Elbow")
1018 layout.operator("mesh.primitive_tee_joint_add",
1019 text="Pipe T-Joint")
1020 layout.operator("mesh.primitive_wye_joint_add",
1021 text="Pipe Y-Joint")
1022 layout.operator("mesh.primitive_cross_joint_add",
1023 text="Pipe Cross-Joint")
1024 layout.operator("mesh.primitive_n_joint_add",
1025 text="Pipe N-Joint")
1027 ################################
1030 # Define "Pipe Joints" menu
1031 def menu_func(self, context):
1032 self.layout.menu("INFO_MT_mesh_pipe_joints_add", icon="PLUGIN")
1035 def register():
1036 bpy.utils.register_module(__name__)
1038 # Add "Pipe Joints" menu to the "Add Mesh" menu
1039 bpy.types.INFO_MT_mesh_add.append(menu_func)
1042 def unregister():
1043 bpy.utils.unregister_module(__name__)
1045 # Remove "Pipe Joints" menu from the "Add Mesh" menu.
1046 bpy.types.INFO_MT_mesh_add.remove(menu_func)
1049 if __name__ == "__main__":
1050 register()