invert scale
[blender-addons.git] / add_mesh_pipe_joint.py
blob9d1fcc0ded9eb5971e649462f8ba7faa60bb0855
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://projects.blender.org/tracker/index.php?"\
30 "func=detail&aid=21443",
31 "category": "Add Mesh"}
33 import bpy
34 from math import *
35 from bpy.props import *
38 # Create a new mesh (object) from verts/edges/faces.
39 # verts/edges/faces ... List of vertices/edges/faces for the
40 # new mesh (as used in from_pydata).
41 # name ... Name of the new mesh (& object).
42 def create_mesh_object(context, verts, edges, faces, name):
43 # Create new mesh
44 mesh = bpy.data.meshes.new(name)
46 # Make a mesh from a list of verts/edges/faces.
47 mesh.from_pydata(verts, edges, faces)
49 # Update mesh geometry after adding stuff.
50 mesh.update()
52 from bpy_extras import object_utils
53 return object_utils.object_data_add(context, mesh, operator=None)
55 # A very simple "bridge" tool.
56 # Connects two equally long vertex rows with faces.
57 # Returns a list of the new faces (list of lists)
59 # vertIdx1 ... First vertex list (list of vertex indices).
60 # vertIdx2 ... Second vertex list (list of vertex indices).
61 # closed ... Creates a loop (first & last are closed).
62 # flipped ... Invert the normal of the face(s).
64 # Note: You can set vertIdx1 to a single vertex index to create
65 # a fan/star of faces.
66 # Note: If both vertex idx list are the same length they have
67 # to have at least 2 vertices.
68 def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
69 faces = []
71 if not vertIdx1 or not vertIdx2:
72 return None
74 if len(vertIdx1) < 2 and len(vertIdx2) < 2:
75 return None
77 fan = False
78 if (len(vertIdx1) != len(vertIdx2)):
79 if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
80 fan = True
81 else:
82 return None
84 total = len(vertIdx2)
86 if closed:
87 # Bridge the start with the end.
88 if flipped:
89 face = [
90 vertIdx1[0],
91 vertIdx2[0],
92 vertIdx2[total - 1]]
93 if not fan:
94 face.append(vertIdx1[total - 1])
95 faces.append(face)
97 else:
98 face = [vertIdx2[0], vertIdx1[0]]
99 if not fan:
100 face.append(vertIdx1[total - 1])
101 face.append(vertIdx2[total - 1])
102 faces.append(face)
104 # Bridge the rest of the faces.
105 for num in range(total - 1):
106 if flipped:
107 if fan:
108 face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
109 else:
110 face = [vertIdx2[num], vertIdx1[num],
111 vertIdx1[num + 1], vertIdx2[num + 1]]
112 faces.append(face)
113 else:
114 if fan:
115 face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
116 else:
117 face = [vertIdx1[num], vertIdx2[num],
118 vertIdx2[num + 1], vertIdx1[num + 1]]
119 faces.append(face)
121 return faces
124 class AddElbowJoint(bpy.types.Operator):
125 # Create the vertices and polygons for a simple elbow (bent pipe).
126 """Add an Elbow pipe mesh"""
127 bl_idname = "mesh.primitive_elbow_joint_add"
128 bl_label = "Add Pipe Elbow"
129 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
131 radius = FloatProperty(name="Radius",
132 description="The radius of the pipe",
133 default=1.0,
134 min=0.01,
135 max=100.0,
136 unit="LENGTH")
137 div = IntProperty(name="Divisions",
138 description="Number of vertices (divisions)",
139 default=32, min=3, max=256)
141 angle = FloatProperty(name="Angle",
142 description="The angle of the branching pipe (i.e. the 'arm' - " \
143 "Measured from the center line of the main pipe",
144 default=radians(45.0),
145 min=radians(-179.9),
146 max=radians(179.9),
147 unit="ROTATION")
149 startLength = FloatProperty(name="Length Start",
150 description="Length of the beginning of the pipe",
151 default=3.0,
152 min=0.01,
153 max=100.0,
154 unit="LENGTH")
155 endLength = FloatProperty(name="End Length",
156 description="Length of the end of the pipe",
157 default=3.0,
158 min=0.01,
159 max=100.0,
160 unit="LENGTH")
162 def execute(self, context):
164 radius = self.radius
165 div = self.div
167 angle = self.angle
169 startLength = self.startLength
170 endLength = self.endLength
172 verts = []
173 faces = []
175 loop1 = [] # The starting circle
176 loop2 = [] # The elbow circle
177 loop3 = [] # The end circle
179 # Create start circle
180 for vertIdx in range(div):
181 curVertAngle = vertIdx * (2.0 * pi / div)
182 locX = sin(curVertAngle)
183 locY = cos(curVertAngle)
184 locZ = -startLength
185 loop1.append(len(verts))
186 verts.append([locX * radius, locY * radius, locZ])
188 # Create deformed joint circle
189 for vertIdx in range(div):
190 curVertAngle = vertIdx * (2.0 * pi / div)
191 locX = sin(curVertAngle)
192 locY = cos(curVertAngle)
193 locZ = locX * tan(angle / 2.0)
194 loop2.append(len(verts))
195 verts.append([locX * radius, locY * radius, locZ * radius])
197 # Create end circle
198 baseEndLocX = -endLength * sin(angle)
199 baseEndLocZ = endLength * cos(angle)
200 for vertIdx in range(div):
201 curVertAngle = vertIdx * (2.0 * pi / div)
202 # Create circle
203 locX = sin(curVertAngle) * radius
204 locY = cos(curVertAngle) * radius
205 locZ = 0.0
207 # Rotate circle
208 locZ = locX * cos(pi / 2.0 - angle)
209 locX = locX * sin(pi / 2.0 - angle)
211 loop3.append(len(verts))
212 # Translate and add circle vertices to the list.
213 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
215 # Create faces
216 faces.extend(createFaces(loop1, loop2, closed=True))
217 faces.extend(createFaces(loop2, loop3, closed=True))
219 base = create_mesh_object(context, verts, [], faces, "Elbow Joint")
221 return {'FINISHED'}
224 class AddTeeJoint(bpy.types.Operator):
225 # Create the vertices and polygons for a simple tee (T) joint.
226 # The base arm of the T can be positioned in an angle if needed though.
227 """Add a Tee-Joint mesh"""
228 bl_idname = "mesh.primitive_tee_joint_add"
229 bl_label = "Add Pipe Tee-Joint"
230 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
232 radius = FloatProperty(name="Radius",
233 description="The radius of the pipe",
234 default=1.0,
235 min=0.01,
236 max=100.0,
237 unit="LENGTH")
238 div = IntProperty(name="Divisions",
239 description="Number of vertices (divisions)",
240 default=32,
241 min=4,
242 max=256)
244 angle = FloatProperty(name="Angle",
245 description="The angle of the branching pipe (i.e. the 'arm' - " \
246 "Measured from the center line of the main pipe",
247 default=radians(90.0),
248 min=radians(0.1),
249 max=radians(179.9),
250 unit="ROTATION")
252 startLength = FloatProperty(name="Length Start",
253 description="Length of the beginning of the" \
254 " main pipe (the straight one)",
255 default=3.0,
256 min=0.01,
257 max=100.0,
258 unit="LENGTH")
259 endLength = FloatProperty(name="End Length",
260 description="Length of the end of the" \
261 " main pipe (the straight one)",
262 default=3.0,
263 min=0.01,
264 max=100.0,
265 unit="LENGTH")
266 branchLength = FloatProperty(name="Arm Length",
267 description="Length of the arm pipe (the bent one)",
268 default=3.0,
269 min=0.01,
270 max=100.0,
271 unit="LENGTH")
273 def execute(self, context):
275 radius = self.radius
276 div = self.div
278 angle = self.angle
280 startLength = self.startLength
281 endLength = self.endLength
282 branchLength = self.branchLength
284 if (div % 2):
285 # Odd vertice number not supported (yet).
286 return {'CANCELLED'}
288 verts = []
289 faces = []
291 # List of vert indices of each cross section
292 loopMainStart = [] # Vert indices for the
293 # beginning of the main pipe.
294 loopJoint1 = [] # Vert indices for joint that is used
295 # to connect the joint & loopMainStart.
296 loopJoint2 = [] # Vert indices for joint that is used
297 # to connect the joint & loopArm.
298 loopJoint3 = [] # Vert index for joint that is used
299 # to connect the joint & loopMainEnd.
300 loopArm = [] # Vert indices for the end of the arm.
301 loopMainEnd = [] # Vert indices for the
302 # end of the main pipe.
304 # Create start circle (main pipe)
305 for vertIdx in range(div):
306 curVertAngle = vertIdx * (2.0 * pi / div)
307 locX = sin(curVertAngle)
308 locY = cos(curVertAngle)
309 locZ = -startLength
310 loopMainStart.append(len(verts))
311 verts.append([locX * radius, locY * radius, locZ])
313 # Create deformed joint circle
314 vertTemp1 = None
315 vertTemp2 = None
316 for vertIdx in range(div):
317 curVertAngle = vertIdx * (2.0 * pi / div)
318 locX = sin(curVertAngle)
319 locY = cos(curVertAngle)
321 if vertIdx == 0:
322 vertTemp1 = len(verts)
323 if vertIdx == div / 2:
324 # @todo: This will possibly break if we
325 # ever support odd divisions.
326 vertTemp2 = len(verts)
328 loopJoint1.append(len(verts))
329 if (vertIdx < div / 2):
330 # Straight side of main pipe.
331 locZ = 0
332 loopJoint3.append(len(verts))
333 else:
334 # Branching side
335 locZ = locX * tan(angle / 2.0)
336 loopJoint2.append(len(verts))
338 verts.append([locX * radius, locY * radius, locZ * radius])
340 # Create 2. deformed joint (half-)circle
341 loopTemp = []
342 for vertIdx in range(div):
343 if (vertIdx > div / 2):
344 curVertAngle = vertIdx * (2.0 * pi / div)
345 locX = sin(curVertAngle)
346 locY = -cos(curVertAngle)
347 locZ = -(radius * locX * tan((pi - angle) / 2.0))
348 loopTemp.append(len(verts))
349 verts.append([locX * radius, locY * radius, locZ])
351 loopTemp2 = loopTemp[:]
353 # Finalise 2. loop
354 loopTemp.reverse()
355 loopTemp.append(vertTemp1)
356 loopJoint2.reverse()
357 loopJoint2.extend(loopTemp)
358 loopJoint2.reverse()
360 # Finalise 3. loop
361 loopTemp2.append(vertTemp2)
362 loopTemp2.reverse()
363 loopJoint3.extend(loopTemp2)
365 # Create end circle (branching pipe)
366 baseEndLocX = -branchLength * sin(angle)
367 baseEndLocZ = branchLength * cos(angle)
368 for vertIdx in range(div):
369 curVertAngle = vertIdx * (2.0 * pi / div)
370 # Create circle
371 locX = sin(curVertAngle) * radius
372 locY = cos(curVertAngle) * radius
373 locZ = 0.0
375 # Rotate circle
376 locZ = locX * cos(pi / 2.0 - angle)
377 locX = locX * sin(pi / 2.0 - angle)
379 loopArm.append(len(verts))
381 # Add translated circle.
382 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
384 # Create end circle (main pipe)
385 for vertIdx in range(div):
386 curVertAngle = vertIdx * (2.0 * pi / div)
387 locX = sin(curVertAngle)
388 locY = cos(curVertAngle)
389 locZ = endLength
390 loopMainEnd.append(len(verts))
391 verts.append([locX * radius, locY * radius, locZ])
393 # Create faces
394 faces.extend(createFaces(loopMainStart, loopJoint1, closed=True))
395 faces.extend(createFaces(loopJoint2, loopArm, closed=True))
396 faces.extend(createFaces(loopJoint3, loopMainEnd, closed=True))
398 base = create_mesh_object(context, verts, [], faces, "Tee Joint")
400 return {'FINISHED'}
403 class AddWyeJoint(bpy.types.Operator):
404 """Add a Wye-Joint mesh"""
405 bl_idname = "mesh.primitive_wye_joint_add"
406 bl_label = "Add Pipe Wye-Joint"
407 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
409 radius = FloatProperty(name="Radius",
410 description="The radius of the pipe",
411 default=1.0,
412 min=0.01,
413 max=100.0,
414 unit="LENGTH")
415 div = IntProperty(name="Divisions",
416 description="Number of vertices (divisions)",
417 default=32,
418 min=4,
419 max=256)
421 angle1 = FloatProperty(name="Angle 1",
422 description="The angle of the 1. branching pipe " \
423 "(measured from the center line of the main pipe)",
424 default=radians(45.0),
425 min=radians(-179.9),
426 max=radians(179.9),
427 unit="ROTATION")
428 angle2 = FloatProperty(name="Angle 2",
429 description="The angle of the 2. branching pipe " \
430 "(measured from the center line of the main pipe) ",
431 default=radians(45.0),
432 min=radians(-179.9),
433 max=radians(179.9),
434 unit="ROTATION")
436 startLength = FloatProperty(name="Length Start",
437 description="Length of the beginning of the" \
438 " main pipe (the straight one)",
439 default=3.0,
440 min=0.01,
441 max=100.0,
442 unit="LENGTH")
443 branch1Length = FloatProperty(name="Length Arm 1",
444 description="Length of the 1. arm",
445 default=3.0,
446 min=0.01,
447 max=100.0,
448 unit="LENGTH")
449 branch2Length = FloatProperty(name="Length Arm 2",
450 description="Length of the 2. arm",
451 default=3.0,
452 min=0.01,
453 max=100.0,
454 unit="LENGTH")
456 def execute(self, context):
458 radius = self.radius
459 div = self.div
461 angle1 = self.angle1
462 angle2 = self.angle2
464 startLength = self.startLength
465 branch1Length = self.branch1Length
466 branch2Length = self.branch2Length
468 if (div % 2):
469 # Odd vertice number not supported (yet).
470 return {'CANCELLED'}
472 verts = []
473 faces = []
475 # List of vert indices of each cross section
476 loopMainStart = [] # Vert indices for
477 # the beginning of the main pipe.
478 loopJoint1 = [] # Vert index for joint that is used
479 # to connect the joint & loopMainStart.
480 loopJoint2 = [] # Vert index for joint that
481 # is used to connect the joint & loopArm1.
482 loopJoint3 = [] # Vert index for joint that is
483 # used to connect the joint & loopArm2.
484 loopArm1 = [] # Vert idxs for end of the 1. arm.
485 loopArm2 = [] # Vert idxs for end of the 2. arm.
487 # Create start circle
488 for vertIdx in range(div):
489 curVertAngle = vertIdx * (2.0 * pi / div)
490 locX = sin(curVertAngle)
491 locY = cos(curVertAngle)
492 locZ = -startLength
493 loopMainStart.append(len(verts))
494 verts.append([locX * radius, locY * radius, locZ])
496 # Create deformed joint circle
497 vertTemp1 = None
498 vertTemp2 = None
499 for vertIdx in range(div):
500 curVertAngle = vertIdx * (2.0 * pi / div)
501 locX = sin(curVertAngle)
502 locY = cos(curVertAngle)
504 if vertIdx == 0:
505 vertTemp2 = len(verts)
506 if vertIdx == div / 2:
507 # @todo: This will possibly break if we
508 # ever support odd divisions.
509 vertTemp1 = len(verts)
511 loopJoint1.append(len(verts))
512 if (vertIdx > div / 2):
513 locZ = locX * tan(angle1 / 2.0)
514 loopJoint2.append(len(verts))
515 else:
516 locZ = locX * tan(-angle2 / 2.0)
517 loopJoint3.append(len(verts))
519 verts.append([locX * radius, locY * radius, locZ * radius])
521 # Create 2. deformed joint (half-)circle
522 loopTemp = []
523 angleJoint = (angle2 - angle1) / 2.0
524 for vertIdx in range(div):
525 if (vertIdx > div / 2):
526 curVertAngle = vertIdx * (2.0 * pi / div)
528 locX = (-sin(curVertAngle) * sin(angleJoint)
529 / sin(angle2 - angleJoint))
530 locY = -cos(curVertAngle)
531 locZ = (-(sin(curVertAngle) * cos(angleJoint)
532 / sin(angle2 - angleJoint)))
534 loopTemp.append(len(verts))
535 verts.append([locX * radius, locY * radius, locZ * radius])
537 loopTemp2 = loopTemp[:]
539 # Finalise 2. loop
540 loopTemp.append(vertTemp1)
541 loopTemp.reverse()
542 loopTemp.append(vertTemp2)
543 loopJoint2.reverse()
544 loopJoint2.extend(loopTemp)
545 loopJoint2.reverse()
547 # Finalise 3. loop
548 loopTemp2.reverse()
549 loopJoint3.extend(loopTemp2)
551 # Create end circle (1. branching pipe)
552 baseEndLocX = -branch1Length * sin(angle1)
553 baseEndLocZ = branch1Length * cos(angle1)
554 for vertIdx in range(div):
555 curVertAngle = vertIdx * (2.0 * pi / div)
556 # Create circle
557 locX = sin(curVertAngle) * radius
558 locY = cos(curVertAngle) * radius
559 locZ = 0.0
561 # Rotate circle
562 locZ = locX * cos(pi / 2.0 - angle1)
563 locX = locX * sin(pi / 2.0 - angle1)
565 loopArm1.append(len(verts))
566 # Add translated circle.
567 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
569 # Create end circle (2. branching pipe)
570 baseEndLocX = branch2Length * sin(angle2)
571 baseEndLocZ = branch2Length * cos(angle2)
572 for vertIdx in range(div):
573 curVertAngle = vertIdx * (2.0 * pi / div)
574 # Create circle
575 locX = sin(curVertAngle) * radius
576 locY = cos(curVertAngle) * radius
577 locZ = 0.0
579 # Rotate circle
580 locZ = locX * cos(pi / 2.0 + angle2)
581 locX = locX * sin(pi / 2.0 + angle2)
583 loopArm2.append(len(verts))
584 # Add translated circle
585 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
587 # Create faces
588 faces.extend(createFaces(loopMainStart, loopJoint1, closed=True))
589 faces.extend(createFaces(loopJoint2, loopArm1, closed=True))
590 faces.extend(createFaces(loopJoint3, loopArm2, closed=True))
592 base = create_mesh_object(context, verts, [], faces, "Wye Joint")
594 return {'FINISHED'}
597 class AddCrossJoint(bpy.types.Operator):
598 """Add a Cross-Joint mesh"""
599 # Create the vertices and polygons for a coss (+ or X) pipe joint.
600 bl_idname = "mesh.primitive_cross_joint_add"
601 bl_label = "Add Pipe Cross-Joint"
602 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
604 radius = FloatProperty(name="Radius",
605 description="The radius of the pipe",
606 default=1.0,
607 min=0.01,
608 max=100.0,
609 unit="LENGTH")
610 div = IntProperty(name="Divisions",
611 description="Number of vertices (divisions)",
612 default=32,
613 min=4,
614 max=256)
616 angle1 = FloatProperty(name="Angle 1",
617 description="The angle of the 1. arm (from the main axis)",
618 default=radians(90.0),
619 min=radians(-179.9),
620 max=radians(179.9),
621 unit="ROTATION")
622 angle2 = FloatProperty(name="Angle 2",
623 description="The angle of the 2. arm (from the main axis)",
624 default=radians(90.0),
625 min=radians(-179.9),
626 max=radians(179.9),
627 unit="ROTATION")
628 angle3 = FloatProperty(name="Angle 3 (center)",
629 description="The angle of the center arm (from the main axis)",
630 default=radians(0.0),
631 min=radians(-179.9),
632 max=radians(179.9),
633 unit="ROTATION")
635 startLength = FloatProperty(name="Length Start",
636 description="Length of the beginning of the " \
637 "main pipe (the straight one)",
638 default=3.0,
639 min=0.01,
640 max=100.0,
641 unit="LENGTH")
642 branch1Length = FloatProperty(name="Length Arm 1",
643 description="Length of the 1. arm",
644 default=3.0,
645 min=0.01,
646 max=100.0,
647 unit="LENGTH")
648 branch2Length = FloatProperty(name="Length Arm 2",
649 description="Length of the 2. arm",
650 default=3.0,
651 min=0.01,
652 max=100.0,
653 unit="LENGTH")
654 branch3Length = FloatProperty(name="Length Arm 3 (center)",
655 description="Length of the center arm",
656 default=3.0,
657 min=0.01,
658 max=100.0,
659 unit="LENGTH")
661 def execute(self, context):
663 radius = self.radius
664 div = self.div
666 angle1 = self.angle1
667 angle2 = self.angle2
668 angle3 = self.angle3
670 startLength = self.startLength
671 branch1Length = self.branch1Length
672 branch2Length = self.branch2Length
673 branch3Length = self.branch3Length
674 if (div % 2):
675 # Odd vertice number not supported (yet).
676 return {'CANCELLED'}
678 verts = []
679 faces = []
681 # List of vert indices of each cross section
682 loopMainStart = [] # Vert indices for the
683 # beginning of the main pipe.
684 loopJoint1 = [] # Vert index for joint that is used
685 # to connect the joint & loopMainStart.
686 loopJoint2 = [] # Vert index for joint that is used
687 # to connect the joint & loopArm1.
688 loopJoint3 = [] # Vert index for joint that is used
689 # to connect the joint & loopArm2.
690 loopJoint4 = [] # Vert index for joint that is used
691 # to connect the joint & loopArm3.
692 loopArm1 = [] # Vert idxs for the end of the 1. arm.
693 loopArm2 = [] # Vert idxs for the end of the 2. arm.
694 loopArm3 = [] # Vert idxs for the center arm end.
696 # Create start circle
697 for vertIdx in range(div):
698 curVertAngle = vertIdx * (2.0 * pi / div)
699 locX = sin(curVertAngle)
700 locY = cos(curVertAngle)
701 locZ = -startLength
702 loopMainStart.append(len(verts))
703 verts.append([locX * radius, locY * radius, locZ])
705 # Create 1. deformed joint circle
706 vertTemp1 = None
707 vertTemp2 = None
708 for vertIdx in range(div):
709 curVertAngle = vertIdx * (2.0 * pi / div)
710 locX = sin(curVertAngle)
711 locY = cos(curVertAngle)
713 if vertIdx == 0:
714 vertTemp2 = len(verts)
715 if vertIdx == div / 2:
716 # @todo: This will possibly break if we
717 # ever support odd divisions.
718 vertTemp1 = len(verts)
720 loopJoint1.append(len(verts))
721 if (vertIdx > div / 2):
722 locZ = locX * tan(angle1 / 2.0)
723 loopJoint2.append(len(verts))
724 else:
725 locZ = locX * tan(-angle2 / 2.0)
726 loopJoint3.append(len(verts))
728 verts.append([locX * radius, locY * radius, locZ * radius])
730 # loopTemp2 = loopJoint2[:] # UNUSED
732 # Create 2. deformed joint circle
733 loopTempA = []
734 loopTempB = []
735 angleJoint1 = (angle1 - angle3) / 2.0
736 angleJoint2 = (angle2 + angle3) / 2.0
737 for vertIdx in range(div):
738 curVertAngle = vertIdx * (2.0 * pi / div)
740 # Skip pole vertices
741 # @todo: This will possibly break if
742 # we ever support odd divisions.
743 if not (vertIdx == 0) and not (vertIdx == div / 2):
745 if (vertIdx > div / 2):
746 angleJoint = angleJoint1
747 angle = angle1
748 Z = -1.0
749 loopTempA.append(len(verts))
751 else:
752 angleJoint = angleJoint2
753 angle = angle2
754 Z = 1.0
755 loopTempB.append(len(verts))
757 locX = (sin(curVertAngle) * sin(angleJoint)
758 / sin(angle - angleJoint))
759 locY = -cos(curVertAngle)
760 locZ = (Z * (sin(curVertAngle) * cos(angleJoint)
761 / sin(angle - angleJoint)))
763 verts.append([locX * radius, locY * radius, locZ * radius])
765 loopTempA2 = loopTempA[:]
766 loopTempB2 = loopTempB[:]
767 loopTempB3 = loopTempB[:]
769 # Finalise 2. loop
770 loopTempA.append(vertTemp1)
771 loopTempA.reverse()
772 loopTempA.append(vertTemp2)
773 loopJoint2.reverse()
774 loopJoint2.extend(loopTempA)
775 loopJoint2.reverse()
777 # Finalise 3. loop
778 loopJoint3.extend(loopTempB3)
780 # Finalise 4. loop
781 loopTempA2.append(vertTemp1)
782 loopTempA2.reverse()
783 loopTempB2.append(vertTemp2)
784 loopJoint4.extend(reversed(loopTempB2))
785 loopJoint4.extend(loopTempA2)
787 # Create end circle (1. branching pipe)
788 baseEndLocX = -branch1Length * sin(angle1)
789 baseEndLocZ = branch1Length * cos(angle1)
790 for vertIdx in range(div):
791 curVertAngle = vertIdx * (2.0 * pi / div)
792 # Create circle
793 locX = sin(curVertAngle) * radius
794 locY = cos(curVertAngle) * radius
795 locZ = 0.0
797 # Rotate circle
798 locZ = locX * cos(pi / 2.0 - angle1)
799 locX = locX * sin(pi / 2.0 - angle1)
801 loopArm1.append(len(verts))
802 # Add translated circle.
803 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
805 # Create end circle (2. branching pipe)
806 baseEndLocX = branch2Length * sin(angle2)
807 baseEndLocZ = branch2Length * cos(angle2)
808 for vertIdx in range(div):
809 curVertAngle = vertIdx * (2.0 * pi / div)
810 # Create circle
811 locX = sin(curVertAngle) * radius
812 locY = cos(curVertAngle) * radius
813 locZ = 0.0
815 # Rotate circle
816 locZ = locX * cos(pi / 2.0 + angle2)
817 locX = locX * sin(pi / 2.0 + angle2)
819 loopArm2.append(len(verts))
820 # Add translated circle
821 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
823 # Create end circle (center pipe)
824 baseEndLocX = branch3Length * sin(angle3)
825 baseEndLocZ = branch3Length * cos(angle3)
826 for vertIdx in range(div):
827 curVertAngle = vertIdx * (2.0 * pi / div)
828 # Create circle
829 locX = sin(curVertAngle) * radius
830 locY = cos(curVertAngle) * radius
831 locZ = 0.0
833 # Rotate circle
834 locZ = locX * cos(pi / 2.0 + angle3)
835 locX = locX * sin(pi / 2.0 + angle3)
837 loopArm3.append(len(verts))
838 # Add translated circle
839 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
841 # Create faces
842 faces.extend(createFaces(loopMainStart, loopJoint1, closed=True))
843 faces.extend(createFaces(loopJoint2, loopArm1, closed=True))
844 faces.extend(createFaces(loopJoint3, loopArm2, closed=True))
845 faces.extend(createFaces(loopJoint4, loopArm3, closed=True))
847 base = create_mesh_object(context, verts, [], faces, "Cross Joint")
849 return {'FINISHED'}
852 class AddNJoint(bpy.types.Operator):
853 """Add a N-Joint mesh"""
854 # Create the vertices and polygons for a regular n-joint.
855 bl_idname = "mesh.primitive_n_joint_add"
856 bl_label = "Add Pipe N-Joint"
857 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
859 radius = FloatProperty(name="Radius",
860 description="The radius of the pipe",
861 default=1.0,
862 min=0.01,
863 max=100.0,
864 unit="LENGTH")
865 div = IntProperty(name="Divisions",
866 description="Number of vertices (divisions)",
867 default=32,
868 min=4,
869 max=256)
870 number = IntProperty(name="Arms/Joints",
871 description="Number of joints/arms",
872 default=5,
873 min=2,
874 max=99999)
875 length = FloatProperty(name="Length",
876 description="Length of each joint/arm",
877 default=3.0,
878 min=0.01,
879 max=100.0,
880 unit="LENGTH")
882 def execute(self, context):
883 radius = self.radius
884 div = self.div
885 number = self.number
886 length = self.length
888 if (div % 2):
889 # Odd vertice number not supported (yet).
890 return {'CANCELLED'}
892 if (number < 2):
893 return {'CANCELLED'}
895 verts = []
896 faces = []
898 loopsEndCircles = []
899 loopsJointsTemp = []
900 loopsJoints = []
902 vertTemp1 = None
903 vertTemp2 = None
905 angleDiv = (2.0 * pi / number)
907 # Create vertices for the end circles.
908 for num in range(number):
909 circle = []
910 # Create start circle
911 angle = num * angleDiv
913 baseEndLocX = length * sin(angle)
914 baseEndLocZ = length * cos(angle)
915 for vertIdx in range(div):
916 curVertAngle = vertIdx * (2.0 * pi / div)
917 # Create circle
918 locX = sin(curVertAngle) * radius
919 locY = cos(curVertAngle) * radius
920 locZ = 0.0
922 # Rotate circle
923 locZ = locX * cos(pi / 2.0 + angle)
924 locX = locX * sin(pi / 2.0 + angle)
926 circle.append(len(verts))
927 # Add translated circle
928 verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ])
930 loopsEndCircles.append(circle)
932 # Create vertices for the joint circles.
933 loopJoint = []
934 for vertIdx in range(div):
935 curVertAngle = vertIdx * (2.0 * pi / div)
936 locX = sin(curVertAngle)
937 locY = cos(curVertAngle)
939 skipVert = False
940 # Store pole vertices
941 if vertIdx == 0:
942 if (num == 0):
943 vertTemp2 = len(verts)
944 else:
945 skipVert = True
946 elif vertIdx == div / 2:
947 # @todo: This will possibly break if we
948 # ever support odd divisions.
949 if (num == 0):
950 vertTemp1 = len(verts)
951 else:
952 skipVert = True
954 if not skipVert:
955 if (vertIdx > div / 2):
956 locZ = -locX * tan((pi - angleDiv) / 2.0)
957 loopJoint.append(len(verts))
959 # Rotate the vert
960 cosAng = cos(-angle)
961 sinAng = sin(-angle)
962 LocXnew = locX * cosAng - locZ * sinAng
963 LocZnew = locZ * cosAng + locX * sinAng
964 locZ = LocZnew
965 locX = LocXnew
967 verts.append([
968 locX * radius,
969 locY * radius,
970 locZ * radius])
971 else:
972 # These two vertices will only be
973 # added the very first time.
974 if vertIdx == 0 or vertIdx == div / 2:
975 verts.append([locX * radius, locY * radius, locZ])
977 loopsJointsTemp.append(loopJoint)
979 # Create complete loops (loopsJoints) out of the
980 # double number of half loops in loopsJointsTemp.
981 for halfLoopIdx in range(len(loopsJointsTemp)):
982 if (halfLoopIdx == len(loopsJointsTemp) - 1):
983 idx1 = halfLoopIdx
984 idx2 = 0
985 else:
986 idx1 = halfLoopIdx
987 idx2 = halfLoopIdx + 1
989 loopJoint = []
990 loopJoint.append(vertTemp2)
991 loopJoint.extend(reversed(loopsJointsTemp[idx2]))
992 loopJoint.append(vertTemp1)
993 loopJoint.extend(loopsJointsTemp[idx1])
995 loopsJoints.append(loopJoint)
997 # Create faces from the two
998 # loop arrays (loopsJoints -> loopsEndCircles).
999 for loopIdx in range(len(loopsEndCircles)):
1000 faces.extend(
1001 createFaces(loopsJoints[loopIdx],
1002 loopsEndCircles[loopIdx], closed=True))
1004 base = create_mesh_object(context, verts, [], faces, "N Joint")
1006 return {'FINISHED'}
1009 class INFO_MT_mesh_pipe_joints_add(bpy.types.Menu):
1010 # Define the "Pipe Joints" menu
1011 bl_idname = "INFO_MT_mesh_pipe_joints_add"
1012 bl_label = "Pipe Joints"
1014 def draw(self, context):
1015 layout = self.layout
1016 layout.operator_context = 'INVOKE_REGION_WIN'
1017 layout.operator("mesh.primitive_elbow_joint_add",
1018 text="Pipe Elbow")
1019 layout.operator("mesh.primitive_tee_joint_add",
1020 text="Pipe T-Joint")
1021 layout.operator("mesh.primitive_wye_joint_add",
1022 text="Pipe Y-Joint")
1023 layout.operator("mesh.primitive_cross_joint_add",
1024 text="Pipe Cross-Joint")
1025 layout.operator("mesh.primitive_n_joint_add",
1026 text="Pipe N-Joint")
1028 ################################
1031 # Define "Pipe Joints" menu
1032 def menu_func(self, context):
1033 self.layout.menu("INFO_MT_mesh_pipe_joints_add", icon="PLUGIN")
1036 def register():
1037 bpy.utils.register_module(__name__)
1039 # Add "Pipe Joints" menu to the "Add Mesh" menu
1040 bpy.types.INFO_MT_mesh_add.append(menu_func)
1043 def unregister():
1044 bpy.utils.unregister_module(__name__)
1046 # Remove "Pipe Joints" menu from the "Add Mesh" menu.
1047 bpy.types.INFO_MT_mesh_add.remove(menu_func)
1050 if __name__ == "__main__":
1051 register()