diff options
author | Brendon Murphy <meta.androcto1@gmail.com> | 2010-03-03 17:43:11 +0300 |
---|---|---|
committer | Brendon Murphy <meta.androcto1@gmail.com> | 2010-03-03 17:43:11 +0300 |
commit | 7f2d06012278090a439df732772e3518b50cec71 (patch) | |
tree | d0c14aad439cf59b7ad72bba0288f3ee08d5d4ae /add_mesh_pipe_joint.py | |
parent | 511909d111b366d633a25ce2a34d1e24eb2d398e (diff) |
add pipe joints script to the add ons folder
Diffstat (limited to 'add_mesh_pipe_joint.py')
-rw-r--r-- | add_mesh_pipe_joint.py | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/add_mesh_pipe_joint.py b/add_mesh_pipe_joint.py new file mode 100644 index 00000000..8ea20402 --- /dev/null +++ b/add_mesh_pipe_joint.py @@ -0,0 +1,1128 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Blender Add-Ons menu registration (in User Prefs) +"Add Pipe Joint (View3D > Add > Mesh > Pipe Joint)" + +import bpy +import Mathutils +from math import * +from bpy.props import * + +""" +Name: 'Pipe Joints' +Blender: 250 +""" +__author__ = ["Buerbaum Martin (Pontiac)"] +__url__ = ["http://gitorious.org/blender-scripts/blender-pipe-joint-script", + "http://blenderartists.org/forum/showthread.php?t=154394", + "http://wiki.blender.org/index.php/Extensions:Py/Scripts/Add/Pipe_Joint"] +__version__ = '0.9.8' +__bpydoc__ = """ +Pipe Joints +This script lets the user create various types of pipe joints. + +Usage: +This functionality can be accessed via the "Add Mesh" -> "Pipe Joints" menu. +Note: Currently only the "Elbow" type supports odd number of vertices. + +Version history: +v0.9.8 - Fixed some new API stuff. + Mainly we now have the register/unregister functions. + Also the new() function for objects now accepts a mesh object. + Corrected FSF address. + Clean up of tooltips. +v0.9.7 - Use "unit" settings for angles as well. + This also lets me use radiant for all internal values.. +v0.9.6 - Use "unit" settings (i.e. none/metric/imperial). +v0.9.5 - Use mesh.from_pydata() for geometry creation. + So we can remove unpack_list and unpack_face_list again. +v0.9.4 - Creating of the pipe now works in mesh edit mode too. + Thanks to ideasman42 (Campbell Barton) for his nice work + on the torus script code :-). +v0.9.3 - Changed to a saner vertex/polygon creation process (previously + my usage of add_geometry could only do quads) + For this I've copied the functions unpack_list and unpack_face_list + from import_scene_obj.py. + Elbow joint actually supports 3 vertices per circle. + Various comments. + Script _should_ now be PEP8 compatible. +v0.9.2 - Converted from tabs to spaces (4 spaces per tab). +v0.9.1 - Converted add_mesh and add_object to their new counterparts + "bpy.data.meshes.new() and "bpy.data.objects.new()" +v0.9 - Converted to 2.5. Made mostly pep8 compatible (exept for tabs and + stuff the check-script didn't catch). +v0.8.5 - Fixed bug in Elbow joint. Same problem as in 0.8.1 +v0.8.4 - Fixed bug in Y joint. Same problem as in 0.8.1 +v0.8.3 - Fixed bug in N joint. Same problem as in 0.8.1 +v0.8.2 - Fixed bug in X (cross) joint. Same problem as in 0.8.1 +v0.8.1 - Fixed bug in T joint. Angles greater than 90 deg combined with a + radius != 1 resulted in bad geometry (the radius was not taken into + account when calculating the joint vertices). +v0.8 - Added N-Joint. + Removed all uses of baseJointLocZ. It just clutters the code. +v0.7 - Added cross joint +v0.6 - No visible changes. Lots of internal ones though + (complete redesign of face creation process). + As a bonus the code is a bit easier to read now. + Added a nice&simple little "bridge" function + (createFaces) for these changes. +v0.5.1 - Made it possible to create asymmetric Y joints. + Renamed the 2 Wye Joints to something more fitting and unique. + One is now the Tee joint, the second one remains the Wye joint. +v0.5 - Added real Y joint. +v0.4.3 - Added check for odd vertex numbers. They are not (yet) supported. +v0.4.2 - Added pipe length to the GUI. +v0.4.1 - Removed the unfinished menu entries for now. +v0.4 - Tried to clean up the face creation in addTeeJoint +v0.3 - Code for wye (Y) shape (straight pipe with "branch" for now) +v0.2 - Restructured to allow different types of pipe (joints). +v0.1 - Initial revision. + +TODO: + +Use a rotation matrix for rotating the circle vertices: +rotation_matrix = Mathutils.RotationMatrix(-math.pi/2, 4, 'x') +mesh.transform(rotation_matrix) +""" + + +def createFaces(vertIdx1, vertIdx2): + ''' + A very simple "bridge" tool. + Connects two equally long vertex-loops with faces and + returns a list of the new faces. + + Parameters + vertIdx1 ... List of vertex indices of the first loop. + vertIdx2 ... List of vertex indices of the second loop. + ''' + faces = [] + + if (len(vertIdx1) != len(vertIdx2)) or (len(vertIdx1) < 2): + return None + + total = len(vertIdx1) + + # Bridge the start with the end. + faces.append([vertIdx2[0], vertIdx1[0], + vertIdx1[total - 1], vertIdx2[total - 1]]) + + # Bridge the rest of the faces. + for num in range(total - 1): + faces.append([vertIdx1[num], vertIdx2[num], + vertIdx2[num + 1], vertIdx1[num + 1]]) + + return faces + + +def createObject(scene, verts, faces, name): + '''Creates Meshes & Objects for the given lists of vertices and faces.''' + + # Create new mesh + mesh = bpy.data.meshes.new(name) + + # Add the geometry to the mesh. + #mesh.add_geometry(len(verts), 0, len(faces)) + #mesh.verts.foreach_set("co", unpack_list(verts)) + #mesh.faces.foreach_set("verts_raw", unpack_face_list(faces)) + + # To quote the documentation: + # "Make a mesh from a list of verts/edges/faces Until we have a nicer + # way to make geometry, use this." + # http://www.blender.org/documentation/250PythonDoc/ + # bpy.types.Mesh.html#bpy.types.Mesh.from_pydata + mesh.from_pydata(verts, [], faces) + + # ugh - Deselect all objects. + for ob in scene.objects: + ob.selected = False + + # Update mesh geometry after adding stuff. + mesh.update() + + # Create a new object. + ob_new = bpy.data.objects.new(name, mesh) + + # Link new object to the given scene and select it. + scene.objects.link(ob_new) + ob_new.selected = True + + # Place the object at the 3D cursor location. + ob_new.location = scene.cursor_location + + obj_act = scene.objects.active + + if obj_act and obj_act.mode == 'EDIT': + # We are in EditMode, switch to ObjectMode. + bpy.ops.object.mode_set(mode='OBJECT') + + # Select the active object as well. + obj_act.selected = True + + # Apply location of new object. + scene.update() + + # Join new object into the active. + bpy.ops.object.join() + + # Switching back to EditMode. + bpy.ops.object.mode_set(mode='EDIT') + + else: + # We are in ObjectMode. + # Make the new object the active one. + scene.objects.active = ob_new + + +class AddElbowJoint(bpy.types.Operator): + # Create the vertices and polygons for a simple elbow (bent pipe). + '''Add an Elbow pipe mesh''' + bl_idname = "mesh.primitive_elbow_joint_add" + bl_label = "Add Pipe Elbow" + bl_options = {'REGISTER', 'UNDO'} + + radius = FloatProperty(name="Radius", + description="The radius of the pipe.", + default=1.0, + min=0.01, + max=100.0, + unit="LENGTH") + div = IntProperty(name="Divisions", + description="Number of vertices (divisions).", + default=32, min=3, max=256) + + angle = FloatProperty(name="Angle", + description="The angle of the branching pipe (i.e. the 'arm')." \ + " Measured from the center line of the main pipe.", + default=radians(45.0), + min=radians(-179.9), + max=radians(179.9), + unit="ROTATION") + + startLength = FloatProperty(name="Length Start", + description="Length of the beginning of the pipe.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + endLength = FloatProperty(name="End Length", + description="Length of the end of the pipe.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + + def execute(self, context): + radius = self.properties.radius + div = self.properties.div + + angle = self.properties.angle + + startLength = self.properties.startLength + endLength = self.properties.endLength + + verts = [] + faces = [] + + loop1 = [] # The starting circle + loop2 = [] # The elbow circle + loop3 = [] # The end circle + + # Create start circle + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + locZ = -startLength + loop1.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ]) + + # Create deformed joint circle + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + locZ = locX * tan(angle / 2.0) + loop2.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ * radius]) + + # Create end circle + baseEndLocX = -endLength * sin(angle) + baseEndLocZ = endLength * cos(angle) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 - angle) + locX = locX * sin(pi / 2.0 - angle) + + loop3.append(len(verts)) + # Translate and add circle vertices to the list. + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create faces + faces.extend(createFaces(loop1, loop2)) + faces.extend(createFaces(loop2, loop3)) + + createObject(context.scene, verts, faces, "Elbow Joint") + + return {'FINISHED'} + + +class AddTeeJoint(bpy.types.Operator): + # Create the vertices and polygons for a simple tee (T) joint. + # The base arm of the T can be positioned in an angle if needed though. + '''Add a Tee-Joint mesh''' + bl_idname = "mesh.primitive_tee_joint_add" + bl_label = "Add Pipe Tee-Joint" + bl_options = {'REGISTER', 'UNDO'} + + radius = FloatProperty(name="Radius", + description="The radius of the pipe.", + default=1.0, + min=0.01, + max=100.0, + unit="LENGTH") + div = IntProperty(name="Divisions", + description="Number of vertices (divisions).", + default=32, + min=4, + max=256) + + angle = FloatProperty(name="Angle", + description="The angle of the branching pipe (i.e. the 'arm')." \ + " Measured from the center line of the main pipe.", + default=radians(90.0), + min=radians(0.1), + max=radians(179.9), + unit="ROTATION") + + startLength = FloatProperty(name="Length Start", + description="Length of the beginning of the" \ + " main pipe (the straight one).", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + endLength = FloatProperty(name="End Length", + description="Length of the end of the" \ + " main pipe (the straight one).", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + branchLength = FloatProperty(name="Arm Length", + description="Length of the arm pipe (the bent one).", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + + def execute(self, context): + radius = self.properties.radius + div = self.properties.div + + angle = self.properties.angle + + startLength = self.properties.startLength + endLength = self.properties.endLength + branchLength = self.properties.branchLength + + if (div % 2): + # Odd vertice number not supported (yet). + return {'CANCELLED'} + + verts = [] + faces = [] + + # List of vert indices of each cross section + loopMainStart = [] # Vert indices for the + # beginning of the main pipe. + loopJoint1 = [] # Vert indices for joint that is used + # to connect the joint & loopMainStart. + loopJoint2 = [] # Vert indices for joint that is used + # to connect the joint & loopArm. + loopJoint3 = [] # Vert index for joint that is used + # to connect the joint & loopMainEnd. + loopArm = [] # Vert indices for the end of the arm. + loopMainEnd = [] # Vert indices for the + # end of the main pipe. + + # Create start circle (main pipe) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + locZ = -startLength + loopMainStart.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ]) + + # Create deformed joint circle + vertTemp1 = None + vertTemp2 = None + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + + if vertIdx == 0: + vertTemp1 = len(verts) + if vertIdx == div / 2: + # @todo: This will possibly break if we + # ever support odd divisions. + vertTemp2 = len(verts) + + loopJoint1.append(len(verts)) + if (vertIdx < div / 2): + # Straight side of main pipe. + locZ = 0 + loopJoint3.append(len(verts)) + else: + # Branching side + locZ = locX * tan(angle / 2.0) + loopJoint2.append(len(verts)) + + verts.append([locX * radius, locY * radius, locZ * radius]) + + # Create 2. deformed joint (half-)circle + loopTemp = [] + for vertIdx in range(div): + if (vertIdx > div / 2): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = -cos(curVertAngle) + locZ = -(radius * locX * tan((pi - angle) / 2.0)) + loopTemp.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ]) + + loopTemp2 = loopTemp[:] + + # Finalise 2. loop + loopTemp.reverse() + loopTemp.append(vertTemp1) + loopJoint2.reverse() + loopJoint2.extend(loopTemp) + loopJoint2.reverse() + + # Finalise 3. loop + loopTemp2.append(vertTemp2) + loopTemp2.reverse() + loopJoint3.extend(loopTemp2) + + # Create end circle (branching pipe) + baseEndLocX = -branchLength * sin(angle) + baseEndLocZ = branchLength * cos(angle) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 - angle) + locX = locX * sin(pi / 2.0 - angle) + + loopArm.append(len(verts)) + + # Add translated circle. + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create end circle (main pipe) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + locZ = endLength + loopMainEnd.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ]) + + # Create faces + faces.extend(createFaces(loopMainStart, loopJoint1)) + faces.extend(createFaces(loopJoint2, loopArm)) + faces.extend(createFaces(loopJoint3, loopMainEnd)) + + createObject(context.scene, verts, faces, "Tee Joint") + + return {'FINISHED'} + + +class AddWyeJoint(bpy.types.Operator): + '''Add a Wye-Joint mesh''' + bl_idname = "mesh.primitive_wye_joint_add" + bl_label = "Add Pipe Wye-Joint" + bl_options = {'REGISTER', 'UNDO'} + + radius = FloatProperty(name="Radius", + description="The radius of the pipe.", + default=1.0, + min=0.01, + max=100.0, + unit="LENGTH") + div = IntProperty(name="Divisions", + description="Number of vertices (divisions).", + default=32, + min=4, + max=256) + + angle1 = FloatProperty(name="Angle 1", + description="The angle of the 1. branching pipe." \ + " Measured from the center line of the main pipe.", + default=radians(45.0), + min=radians(-179.9), + max=radians(179.9), + unit="ROTATION") + angle2 = FloatProperty(name="Angle 2", + description="The angle of the 2. branching pipe." \ + " Measured from the center line of the main pipe.", + default=radians(45.0), + min=radians(-179.9), + max=radians(179.9), + unit="ROTATION") + + startLength = FloatProperty(name="Length Start", + description="Length of the beginning of the" \ + " main pipe (the straight one).", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + branch1Length = FloatProperty(name="Length Arm 1", + description="Length of the 1. arm.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + branch2Length = FloatProperty(name="Length Arm 2", + description="Length of the 2. arm.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + + def execute(self, context): + radius = self.properties.radius + div = self.properties.div + + angle1 = self.properties.angle1 + angle2 = self.properties.angle2 + + startLength = self.properties.startLength + branch1Length = self.properties.branch1Length + branch2Length = self.properties.branch2Length + + if (div % 2): + # Odd vertice number not supported (yet). + return {'CANCELLED'} + + verts = [] + faces = [] + + # List of vert indices of each cross section + loopMainStart = [] # Vert indices for + # the beginning of the main pipe. + loopJoint1 = [] # Vert index for joint that is used + # to connect the joint & loopMainStart. + loopJoint2 = [] # Vert index for joint that + # is used to connect the joint & loopArm1. + loopJoint3 = [] # Vert index for joint that is + # used to connect the joint & loopArm2. + loopArm1 = [] # Vert idxs for end of the 1. arm. + loopArm2 = [] # Vert idxs for end of the 2. arm. + + # Create start circle + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + locZ = -startLength + loopMainStart.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ]) + + # Create deformed joint circle + vertTemp1 = None + vertTemp2 = None + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + + if vertIdx == 0: + vertTemp2 = len(verts) + if vertIdx == div / 2: + # @todo: This will possibly break if we + # ever support odd divisions. + vertTemp1 = len(verts) + + loopJoint1.append(len(verts)) + if (vertIdx > div / 2): + locZ = locX * tan(angle1 / 2.0) + loopJoint2.append(len(verts)) + else: + locZ = locX * tan(-angle2 / 2.0) + loopJoint3.append(len(verts)) + + verts.append([locX * radius, locY * radius, locZ * radius]) + + # Create 2. deformed joint (half-)circle + loopTemp = [] + angleJoint = (angle2 - angle1) / 2.0 + for vertIdx in range(div): + if (vertIdx > div / 2): + curVertAngle = vertIdx * (2.0 * pi / div) + + locX = (-sin(curVertAngle) * sin(angleJoint) + / sin(angle2 - angleJoint)) + locY = -cos(curVertAngle) + locZ = (-(sin(curVertAngle) * cos(angleJoint) + / sin(angle2 - angleJoint))) + + loopTemp.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ * radius]) + + loopTemp2 = loopTemp[:] + + # Finalise 2. loop + loopTemp.append(vertTemp1) + loopTemp.reverse() + loopTemp.append(vertTemp2) + loopJoint2.reverse() + loopJoint2.extend(loopTemp) + loopJoint2.reverse() + + # Finalise 3. loop + loopTemp2.reverse() + loopJoint3.extend(loopTemp2) + + # Create end circle (1. branching pipe) + baseEndLocX = -branch1Length * sin(angle1) + baseEndLocZ = branch1Length * cos(angle1) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 - angle1) + locX = locX * sin(pi / 2.0 - angle1) + + loopArm1.append(len(verts)) + # Add translated circle. + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create end circle (2. branching pipe) + baseEndLocX = branch2Length * sin(angle2) + baseEndLocZ = branch2Length * cos(angle2) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 + angle2) + locX = locX * sin(pi / 2.0 + angle2) + + loopArm2.append(len(verts)) + # Add translated circle + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create faces + faces.extend(createFaces(loopMainStart, loopJoint1)) + faces.extend(createFaces(loopJoint2, loopArm1)) + faces.extend(createFaces(loopJoint3, loopArm2)) + + createObject(context.scene, verts, faces, "Wye Joint") + + return {'FINISHED'} + + +class AddCrossJoint(bpy.types.Operator): + '''Add a Cross-Joint mesh''' + # Create the vertices and polygons for a coss (+ or X) pipe joint. + bl_idname = "mesh.primitive_cross_joint_add" + bl_label = "Add Pipe Cross-Joint" + bl_options = {'REGISTER', 'UNDO'} + + radius = FloatProperty(name="Radius", + description="The radius of the pipe.", + default=1.0, + min=0.01, + max=100.0, + unit="LENGTH") + div = IntProperty(name="Divisions", + description="Number of vertices (divisions).", + default=32, + min=4, + max=256) + + angle1 = FloatProperty(name="Angle 1", + description="The angle of the 1. arm (from the main axis).", + default=radians(90.0), + min=radians(-179.9), + max=radians(179.9), + unit="ROTATION") + angle2 = FloatProperty(name="Angle 2", + description="The angle of the 2. arm (from the main axis).", + default=radians(90.0), + min=radians(-179.9), + max=radians(179.9), + unit="ROTATION") + angle3 = FloatProperty(name="Angle 3 (center)", + description="The angle of the center arm (from the main axis).", + default=radians(0.0), + min=radians(-179.9), + max=radians(179.9), + unit="ROTATION") + + startLength = FloatProperty(name="Length Start", + description="Length of the beginning of the " \ + "main pipe (the straight one).", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + branch1Length = FloatProperty(name="Length Arm 1", + description="Length of the 1. arm.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + branch2Length = FloatProperty(name="Length Arm 2", + description="Length of the 2. arm.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + branch3Length = FloatProperty(name="Length Arm 3 (center)", + description="Length of the center arm.", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + + def execute(self, context): + radius = self.properties.radius + div = self.properties.div + + angle1 = self.properties.angle1 + angle2 = self.properties.angle2 + angle3 = self.properties.angle3 + + startLength = self.properties.startLength + branch1Length = self.properties.branch1Length + branch2Length = self.properties.branch2Length + branch3Length = self.properties.branch3Length + if (div % 2): + # Odd vertice number not supported (yet). + return {'CANCELLED'} + + verts = [] + faces = [] + + # List of vert indices of each cross section + loopMainStart = [] # Vert indices for the + # beginning of the main pipe. + loopJoint1 = [] # Vert index for joint that is used + # to connect the joint & loopMainStart. + loopJoint2 = [] # Vert index for joint that is used + # to connect the joint & loopArm1. + loopJoint3 = [] # Vert index for joint that is used + # to connect the joint & loopArm2. + loopJoint4 = [] # Vert index for joint that is used + # to connect the joint & loopArm3. + loopArm1 = [] # Vert idxs for the end of the 1. arm. + loopArm2 = [] # Vert idxs for the end of the 2. arm. + loopArm3 = [] # Vert idxs for the center arm end. + + # Create start circle + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + locZ = -startLength + loopMainStart.append(len(verts)) + verts.append([locX * radius, locY * radius, locZ]) + + # Create 1. deformed joint circle + vertTemp1 = None + vertTemp2 = None + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + + if vertIdx == 0: + vertTemp2 = len(verts) + if vertIdx == div / 2: + # @todo: This will possibly break if we + # ever support odd divisions. + vertTemp1 = len(verts) + + loopJoint1.append(len(verts)) + if (vertIdx > div / 2): + locZ = locX * tan(angle1 / 2.0) + loopJoint2.append(len(verts)) + else: + locZ = locX * tan(-angle2 / 2.0) + loopJoint3.append(len(verts)) + + verts.append([locX * radius, locY * radius, locZ * radius]) + + loopTemp2 = loopJoint2[:] + + # Create 2. deformed joint circle + loopTempA = [] + loopTempB = [] + angleJoint1 = (angle1 - angle3) / 2.0 + angleJoint2 = (angle2 + angle3) / 2.0 + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + + # Skip pole vertices + # @todo: This will possibly break if + # we ever support odd divisions. + if not (vertIdx == 0) and not (vertIdx == div / 2): + + if (vertIdx > div / 2): + angleJoint = angleJoint1 + angle = angle1 + Z = -1.0 + loopTempA.append(len(verts)) + + else: + angleJoint = angleJoint2 + angle = angle2 + Z = 1.0 + loopTempB.append(len(verts)) + + locX = (sin(curVertAngle) * sin(angleJoint) + / sin(angle - angleJoint)) + locY = -cos(curVertAngle) + locZ = (Z * (sin(curVertAngle) * cos(angleJoint) + / sin(angle - angleJoint))) + + verts.append([locX * radius, locY * radius, locZ * radius]) + + loopTempA2 = loopTempA[:] + loopTempB2 = loopTempB[:] + loopTempB3 = loopTempB[:] + + # Finalise 2. loop + loopTempA.append(vertTemp1) + loopTempA.reverse() + loopTempA.append(vertTemp2) + loopJoint2.reverse() + loopJoint2.extend(loopTempA) + loopJoint2.reverse() + + # Finalise 3. loop + loopJoint3.extend(loopTempB3) + + # Finalise 4. loop + loopTempA2.append(vertTemp1) + loopTempA2.reverse() + loopTempB2.append(vertTemp2) + loopJoint4.extend(reversed(loopTempB2)) + loopJoint4.extend(loopTempA2) + + # Create end circle (1. branching pipe) + baseEndLocX = -branch1Length * sin(angle1) + baseEndLocZ = branch1Length * cos(angle1) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 - angle1) + locX = locX * sin(pi / 2.0 - angle1) + + loopArm1.append(len(verts)) + # Add translated circle. + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create end circle (2. branching pipe) + baseEndLocX = branch2Length * sin(angle2) + baseEndLocZ = branch2Length * cos(angle2) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 + angle2) + locX = locX * sin(pi / 2.0 + angle2) + + loopArm2.append(len(verts)) + # Add translated circle + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create end circle (center pipe) + baseEndLocX = branch3Length * sin(angle3) + baseEndLocZ = branch3Length * cos(angle3) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 + angle3) + locX = locX * sin(pi / 2.0 + angle3) + + loopArm3.append(len(verts)) + # Add translated circle + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + # Create faces + faces.extend(createFaces(loopMainStart, loopJoint1)) + faces.extend(createFaces(loopJoint2, loopArm1)) + faces.extend(createFaces(loopJoint3, loopArm2)) + faces.extend(createFaces(loopJoint4, loopArm3)) + + createObject(context.scene, verts, faces, "Cross Joint") + + return {'FINISHED'} + + +class AddNJoint(bpy.types.Operator): + '''Add a N-Joint mesh''' + # Create the vertices and polygons for a regular n-joint. + bl_idname = "mesh.primitive_n_joint_add" + bl_label = "Add Pipe N-Joint" + bl_options = {'REGISTER', 'UNDO'} + + radius = FloatProperty(name="Radius", + description="The radius of the pipe.", + default=1.0, + min=0.01, + max=100.0, + unit="LENGTH") + div = IntProperty(name="Divisions", + description="Number of vertices (divisions).", + default=32, + min=4, + max=256) + number = IntProperty(name="Arms/Joints", + description="Number of joints/arms", + default=5, + min=2, + max=99999) + length = FloatProperty(name="Length", + description="Length of each joint/arm", + default=3.0, + min=0.01, + max=100.0, + unit="LENGTH") + + def execute(self, context): + radius = self.properties.radius + div = self.properties.div + number = self.properties.number + length = self.properties.length + + if (div % 2): + # Odd vertice number not supported (yet). + return {'CANCELLED'} + + if (number < 2): + return {'CANCELLED'} + + verts = [] + faces = [] + + loopsEndCircles = [] + loopsJointsTemp = [] + loopsJoints = [] + + vertTemp1 = None + vertTemp2 = None + + angleDiv = (2.0 * pi / number) + + # Create vertices for the end circles. + for num in range(number): + circle = [] + # Create start circle + angle = num * angleDiv + + baseEndLocX = length * sin(angle) + baseEndLocZ = length * cos(angle) + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + # Create circle + locX = sin(curVertAngle) * radius + locY = cos(curVertAngle) * radius + locZ = 0.0 + + # Rotate circle + locZ = locX * cos(pi / 2.0 + angle) + locX = locX * sin(pi / 2.0 + angle) + + circle.append(len(verts)) + # Add translated circle + verts.append([baseEndLocX + locX, locY, baseEndLocZ + locZ]) + + loopsEndCircles.append(circle) + + # Create vertices for the joint circles. + loopJoint = [] + for vertIdx in range(div): + curVertAngle = vertIdx * (2.0 * pi / div) + locX = sin(curVertAngle) + locY = cos(curVertAngle) + + skipVert = False + # Store pole vertices + if vertIdx == 0: + if (num == 0): + vertTemp2 = len(verts) + else: + skipVert = True + elif vertIdx == div / 2: + # @todo: This will possibly break if we + # ever support odd divisions. + if (num == 0): + vertTemp1 = len(verts) + else: + skipVert = True + + if not skipVert: + if (vertIdx > div / 2): + locZ = -locX * tan((pi - angleDiv) / 2.0) + loopJoint.append(len(verts)) + + # Rotate the vert + cosAng = cos(-angle) + sinAng = sin(-angle) + LocXnew = locX * cosAng - locZ * sinAng + LocZnew = locZ * cosAng + locX * sinAng + locZ = LocZnew + locX = LocXnew + + verts.append([ + locX * radius, + locY * radius, + locZ * radius]) + else: + # These two vertices will only be + # added the very first time. + if vertIdx == 0 or vertIdx == div / 2: + verts.append([locX * radius, locY * radius, locZ]) + + loopsJointsTemp.append(loopJoint) + + # Create complete loops (loopsJoints) out of the + # double number of half loops in loopsJointsTemp. + for halfLoopIdx in range(len(loopsJointsTemp)): + if (halfLoopIdx == len(loopsJointsTemp) - 1): + idx1 = halfLoopIdx + idx2 = 0 + else: + idx1 = halfLoopIdx + idx2 = halfLoopIdx + 1 + + loopJoint = [] + loopJoint.append(vertTemp2) + loopJoint.extend(reversed(loopsJointsTemp[idx2])) + loopJoint.append(vertTemp1) + loopJoint.extend(loopsJointsTemp[idx1]) + + loopsJoints.append(loopJoint) + + # Create faces from the two + # loop arrays (loopsJoints -> loopsEndCircles). + for loopIdx in range(len(loopsEndCircles)): + faces.extend( + createFaces(loopsJoints[loopIdx], + loopsEndCircles[loopIdx])) + + createObject(context.scene, verts, faces, "N Joint") + + return {'FINISHED'} + + +class INFO_MT_mesh_pipe_joints_add(bpy.types.Menu): + # Define the "Pipe Joints" menu + bl_idname = "INFO_MT_mesh_pipe_joints_add" + bl_label = "Pipe Joints" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("mesh.primitive_elbow_joint_add", + text="Pipe Elbow") + layout.operator("mesh.primitive_tee_joint_add", + text="Pipe T-Joint") + layout.operator("mesh.primitive_wye_joint_add", + text="Pipe Y-Joint") + layout.operator("mesh.primitive_cross_joint_add", + text="Pipe Cross-Joint") + layout.operator("mesh.primitive_n_joint_add", + text="Pipe N-Joint" ) +# layout.separator() + +################################ + +import space_info + +# Define "Pipe Joints" menu +# Define "Pipe Joints" menu +menu_func = (lambda self, + context: self.layout.menu("INFO_MT_mesh_pipe_joints_add", icon='GEARS')) + + +def register(): + # Register the operators/menus. + bpy.types.register(AddElbowJoint) + bpy.types.register(AddTeeJoint) + bpy.types.register(AddWyeJoint) + bpy.types.register(AddCrossJoint) + bpy.types.register(AddNJoint) + bpy.types.register(INFO_MT_mesh_pipe_joints_add) + + # Add "Pipe Joints" menu to the "Add Mesh" menu + space_info.INFO_MT_mesh_add.append(menu_func) + + +def unregister(): + # Unregister the operators/menus. + bpy.types.unregister(AddElbowJoint) + bpy.types.unregister(AddTeeJoint) + bpy.types.unregister(AddWyeJoint) + bpy.types.unregister(AddCrossJoint) + bpy.types.unregister(AddNJoint) + bpy.types.unregister(INFO_MT_mesh_pipe_joints_add) + + # Remove "Pipe Joints" menu from the "Add Mesh" menu. + #space_info.INFO_MT_mesh_add.remove(menu_func) + |