Gist URL: https://gist.github.com/stephensmitchell/6885e8b6986fde49cced41f1e3327cd8
- Joint Creator.py
- JointCreatorIcon.png
- Pocket Hole Creator.py
- PocketHoleCreatorIcon.png
Gist URL: https://gist.github.com/stephensmitchell/6885e8b6986fde49cced41f1e3327cd8
| # Joint Creator | |
| # (c) Alibre, LLC 2019, All Rights Reserved | |
| # Version 1.00 | |
| from __future__ import division | |
| from math import * | |
| # gets locaton of edge in a part coordinate system | |
| # returns a list of two points defining the edge | |
| def GetPartEdge(Prt, SharedEdge): | |
| Point1 = Prt.AssemblyPointtoPartPoint(SharedEdge[0]) | |
| Point2 = Prt.AssemblyPointtoPartPoint(SharedEdge[1]) | |
| return [Point1, Point2] | |
| # compares two points [X1, Y1, Z1] and [X2, Y2, Z2] | |
| # returns true if they are the same | |
| def PointsAreEqual(P1, P2): | |
| if (round(P1[0], 6) == round(P2[0], 6) and | |
| round(P1[1], 6) == round(P2[1], 6) and | |
| round(P1[2], 6) == round(P2[2], 6)): | |
| return True | |
| else: | |
| return False | |
| # gets part faces that use an edge | |
| # returns a list of faces | |
| def GetFacesFromEdge(Prt, SharedEdge): | |
| Faces = [] | |
| PartEdge = GetPartEdge(Prt, SharedEdge) | |
| for Fce in Prt.Faces: | |
| for Edg in Fce.GetEdges(): | |
| EdgeVertices = Edg.GetVertices() | |
| V1 = [EdgeVertices[0].X, EdgeVertices[0].Y, EdgeVertices[0].Z] | |
| V2 = [EdgeVertices[1].X, EdgeVertices[1].Y, EdgeVertices[1].Z] | |
| if ((PointsAreEqual(V1, PartEdge[0]) and PointsAreEqual(V2, PartEdge[1])) or | |
| (PointsAreEqual(V2, PartEdge[0]) and PointsAreEqual(V1, PartEdge[1]))): | |
| Faces.append(Fce) | |
| return Faces | |
| # gets an edge that is shared between two parts | |
| # returns list of edge vertices | |
| def GetSharedEdge(Prt1, Prt2): | |
| CornerVertices = [] | |
| for TabVert in Prt1.GetAssemblyVertices(): | |
| for BaseVert in Prt2.GetAssemblyVertices(): | |
| if PointsAreEqual(TabVert, BaseVert): | |
| CornerVertices.append(TabVert) | |
| return CornerVertices | |
| # gets the length of an edge | |
| # returns edge length | |
| def GetEdgeLength(Vert1, Vert2): | |
| a = abs(Vert2[0] - Vert1[0]) | |
| b = abs(Vert2[1] - Vert1[1]) | |
| c = abs(Vert2[2] - Vert1[2]) | |
| return sqrt(a * a + b * b + c * c) | |
| # gets the largest face from a set of faces | |
| def GetLargestFace(Faces): | |
| if Faces[0].GetArea() > Faces[1].GetArea(): | |
| return Faces[0] | |
| elif Faces[1].GetArea() > Faces[0].GetArea(): | |
| return Faces[1] | |
| else: | |
| print "Unable to determine face of part." | |
| sys.exit() | |
| # gets the smallest face from a set of faces | |
| def GetSmallestFace(Faces): | |
| if Faces[0].GetArea() < Faces[1].GetArea(): | |
| return Faces[0] | |
| elif Faces[1].GetArea() < Faces[0].GetArea(): | |
| return Faces[1] | |
| else: | |
| print "Unable to determine face of part." | |
| sys.exit() | |
| # generates a range of real values from start to stop | |
| # incremented by step | |
| def frange(start, stop, step): | |
| i = start | |
| if start < stop: | |
| while i < stop: | |
| yield i | |
| i += step | |
| else: | |
| while i > stop: | |
| yield i | |
| i += step | |
| # gets the shortest edge of a face | |
| # returns shortest edge | |
| def GetShortestEdge(Fce): | |
| Shortest = Fce.GetEdges()[0] | |
| for E in Fce.GetEdges(): | |
| if E.Length < Shortest.Length: | |
| Shortest = E | |
| return Shortest | |
| # generates pin offsets | |
| # NumPins = number of pins | |
| # EdgeLength = length of edge for pins | |
| # PinSense = True = slot at edge, False = pin at edge | |
| # EdgeOffset = distance from ends of edges before pins | |
| # Gap = distance between slot and pin | |
| # returns: [ [Pin_1_Start, Pin_1_End], ..., [Pin_n_Start, Pin_n_End] ] | |
| def GeneratePinOffsets(NumPins, EdgeLength, PinSense, EdgeOffset, Gap): | |
| Offsets = [] | |
| # reduce length of edge by the edge offset at each end | |
| # giving a length that we generate pins and slots over | |
| PinEdgeLength = EdgeLength - (EdgeOffset * 2) | |
| # get length of each pin | |
| if PinSense == False: | |
| PinLength = PinEdgeLength / (NumPins + (NumPins - 1)) | |
| PinState = True | |
| else: | |
| PinLength = PinEdgeLength / (NumPins + (NumPins + 1)) | |
| PinState = False | |
| # generate start and end point of each pin | |
| CurrentPin = 0 | |
| for Y in frange(EdgeOffset, EdgeLength - EdgeOffset, PinLength): | |
| if PinState: | |
| # if pins are never at the edges then always use gap on each | |
| # side of pin | |
| if PinSense == True: | |
| Offsets.append([Y - Gap, Y + PinLength + Gap]) | |
| # pins could be at edges where we don't want the gap to be applied | |
| else: | |
| if CurrentPin == 0: | |
| # first pin, no gap at start | |
| Offsets.append([Y, Y + PinLength + Gap]) | |
| elif CurrentPin == NumPins - 1: | |
| # last pin, no gap at end | |
| Offsets.append([Y - Gap, Y + PinLength]) | |
| else: | |
| # middle pin, gap at start and end | |
| Offsets.append([Y - Gap, Y + PinLength + Gap]) | |
| CurrentPin += 1 | |
| PinState = not PinState | |
| return Offsets | |
| # generates slot offsets | |
| # NumPins = number of pins | |
| # EdgeLength = length of edge for slots | |
| # PinSense = True = slot at edge, False = pin at edge | |
| # EdgeOffset = distance from ends of edges before pins | |
| # Gap = distance between slot and pin | |
| # returns: [ [Slot_1_Start, Slot_1_End], ..., [Slot_n_Start, Slot_n_End] ] | |
| def GenerateSlotOffsets(NumPins, EdgeLength, PinSense, EdgeOffset, Gap): | |
| Offsets = [] | |
| # reduce length of edge by the edge offset at each end | |
| # giving a length that we generate pins and slots over | |
| PinEdgeLength = EdgeLength - (EdgeOffset * 2) | |
| # get length of each pin | |
| if PinSense == False: | |
| PinLength = PinEdgeLength / (NumPins + (NumPins - 1)) | |
| PinState = False | |
| else: | |
| PinLength = PinEdgeLength / (NumPins + (NumPins + 1)) | |
| PinState = True | |
| if PinSense == True: | |
| NumSlots = NumPins + 1 | |
| else: | |
| NumSlots = NumPins - 1 | |
| # add initial slot for edge offset if pins are on outside of slots | |
| if EdgeOffset > 0 and PinSense == False: | |
| Offsets.append([0, EdgeOffset + (Gap * 2.0)]) | |
| # generate start and end point of each slot | |
| CurrentSlot = 0 | |
| for Y in frange(EdgeOffset, EdgeLength - EdgeOffset, PinLength): | |
| if PinState: | |
| # if slots are never at the edges then always use gap on each | |
| # side of slot | |
| if PinSense == False or (EdgeOffset > 0): | |
| Offsets.append([Y - Gap, Y + PinLength + Gap]) | |
| # slots could be at edges where we don't want the gap to be applied | |
| else: | |
| if CurrentSlot == 0: | |
| # first slot, no gap at start | |
| Offsets.append([Y, Y + PinLength + Gap]) | |
| elif CurrentSlot == NumSlots - 1: | |
| # last slot, no gap at end | |
| Offsets.append([Y - Gap, Y + PinLength]) | |
| else: | |
| # middle pin, gap at start and end | |
| Offsets.append([Y - Gap, Y + PinLength + Gap]) | |
| CurrentSlot += 1 | |
| PinState = not PinState | |
| # add final slot for edge offset if pins are on outside of slots | |
| if EdgeOffset > 0 and PinSense == False: | |
| Offsets.append([EdgeLength - EdgeOffset - (Gap * 2.0), EdgeLength]) | |
| if EdgeOffset > 0 and PinSense == True: | |
| # extend first slot to cover edge offset | |
| Offsets[0][0] = 0 | |
| # extend last slot to cover edge offset | |
| Offsets[len(Offsets) - 1][1] = EdgeLength | |
| return Offsets | |
| # generates the pins | |
| # Prt = part to create pins on | |
| # Fce = face on part to create pins | |
| # PinOffsets = start and end values for pins | |
| # Thickness = depth of pins | |
| # SharedEdge = edge to generate pins along | |
| def GeneratePins(Prt, Fce, PinOffsets, Thickness, SharedEdge): | |
| TabProfile = Prt.AddSketch('Pin Profile', Fce) | |
| TabEdge = GetPartEdge(Prt, SharedEdge) | |
| TabProfile.StartFaceMapping(TabEdge[0], TabEdge[1]) | |
| for PinOffset in PinOffsets: | |
| TabProfile.AddRectangle(PinOffset[0], 0, PinOffset[1], Thickness, False) | |
| TabProfile.StopFaceMapping() | |
| # cut out rectangles (pins) | |
| Prt.AddExtrudeCut('Pins', TabProfile, 0, False, Part.EndCondition.ThroughAll, None, 0, Part.DirectionType.Normal, None, 0, False) | |
| # generates the slots | |
| # Prt = part to create slots on | |
| # Fce = face on part to create slots | |
| # SlotOffsets = start and end values for slots | |
| # Thickness = depth of slots | |
| # SharedEdge = edge to generate slots along | |
| def GenerateSlots(Prt, Fce, SlotOffsets, Thickness, SharedEdge): | |
| BaseProfile = Prt.AddSketch('Slot Profile', Fce) | |
| BaseEdge = GetPartEdge(Prt, SharedEdge) | |
| BaseProfile.StartFaceMapping(BaseEdge[0], BaseEdge[1]) | |
| for SlotOffset in SlotOffsets: | |
| BaseProfile.AddRectangle(SlotOffset[0], 0, SlotOffset[1], Thickness, False) | |
| BaseProfile.StopFaceMapping() | |
| # cut out rectangles (slots) | |
| Prt.AddExtrudeCut('Slots', BaseProfile, 0, False, Part.EndCondition.ThroughAll, None, 0, Part.DirectionType.Normal, None, 0, False) | |
| # creates a joint based on user inputs | |
| def CreateJoint(Values): | |
| TabPart = Values[1] | |
| BasePart = Values[2] | |
| NumberofPins = Values[3] | |
| PinSense = Values[4] | |
| EdgeOffset = Values[5] | |
| Gap = Values[6] | |
| print "Gathering information..." | |
| # get edge shared by both parts | |
| SharedEdge = GetSharedEdge(TabPart, BasePart) | |
| # get the part faces for the shared edge | |
| TabFaces = GetFacesFromEdge(TabPart, SharedEdge) | |
| BaseFaces = GetFacesFromEdge(BasePart, SharedEdge) | |
| # get the largest faces on each part that use the shared edge | |
| TabFace = GetLargestFace(TabFaces) | |
| BaseFace = GetLargestFace(BaseFaces) | |
| # the smallest faces on each part that use the shared edge | |
| TabEndFace = GetSmallestFace(TabFaces) | |
| BaseEndFace = GetSmallestFace(BaseFaces) | |
| # get length of shared edge | |
| SharedEdgeLength = GetEdgeLength(SharedEdge[0], SharedEdge[1]) | |
| # get thickness of each part | |
| TabThickness = GetShortestEdge(TabEndFace).Length | |
| BaseThickness = GetShortestEdge(BaseEndFace).Length | |
| print "Calculating..." | |
| # generate pin and slot offsets | |
| PinOffsets = GeneratePinOffsets(NumberofPins, SharedEdgeLength, PinSense, EdgeOffset, Gap / 2.0) | |
| SlotOffsets = GenerateSlotOffsets(NumberofPins, SharedEdgeLength, PinSense, EdgeOffset, Gap / 2.0) | |
| print "Generating..." | |
| # generate pins and slots | |
| GeneratePins(TabPart, TabFace, PinOffsets, BaseThickness, SharedEdge) | |
| GenerateSlots(BasePart, BaseFace, SlotOffsets, TabThickness, SharedEdge) | |
| print "Finished" | |
| ################################################################################################# | |
| # check minimum requirements | |
| if AlibreScriptVersion < 1110: | |
| sys.exit('Please upgrade your copy of Alibre Design to use this script') | |
| ScriptName = 'Joint Creator' | |
| Win = Windows() | |
| # define options to show in dialog window | |
| Options = [] | |
| Options.append([None, WindowsInputTypes.Image, 'JointCreatorIcon.png', 200]) | |
| Options.append(['Tab Part', WindowsInputTypes.Part, None]) | |
| Options.append(['Base Part', WindowsInputTypes.Part, None]) | |
| Options.append(['Number of Pins', WindowsInputTypes.Integer, 5]) | |
| Options.append(['Pins on Inside', WindowsInputTypes.Boolean, False]) | |
| Options.append(['Offset From Ends', WindowsInputTypes.Real, 0.0]) | |
| Options.append(['Gap Between Pins and Slots', WindowsInputTypes.Real, 0.0]) | |
| # show utility window | |
| Win.UtilityDialog(ScriptName, 'Create Joint', CreateJoint, None, Options, 200) |
| # Pocket Hole Creator | |
| # (c) Alibre, LLC 2019, All Rights Reserved | |
| # Version 1.00 | |
| from __future__ import division | |
| from math import * | |
| # compares two points [X1, Y1, Z1] and [X2, Y2, Z2] | |
| # returns true if they are the same | |
| def PointsAreEqual(P1, P2): | |
| if (round(P1[0], 6) == round(P2[0], 6) and | |
| round(P1[1], 6) == round(P2[1], 6) and | |
| round(P1[2], 6) == round(P2[2], 6)): | |
| return True | |
| else: | |
| return False | |
| # gets part faces that use an edge | |
| # returns a list of faces | |
| def GetFacesFromEdge(Prt, Ege): | |
| Faces = [] | |
| PartEdge = [[Ege.Vertices[0].X, Ege.Vertices[0].Y, Ege.Vertices[0].Z], [Ege.Vertices[1].X, Ege.Vertices[1].Y, Ege.Vertices[1].Z]] | |
| for Fce in Prt.Faces: | |
| for Edg in Fce.GetEdges(): | |
| EdgeVertices = Edg.GetVertices() | |
| V1 = [EdgeVertices[0].X, EdgeVertices[0].Y, EdgeVertices[0].Z] | |
| V2 = [EdgeVertices[1].X, EdgeVertices[1].Y, EdgeVertices[1].Z] | |
| if ((PointsAreEqual(V1, PartEdge[0]) and PointsAreEqual(V2, PartEdge[1])) or | |
| (PointsAreEqual(V2, PartEdge[0]) and PointsAreEqual(V1, PartEdge[1]))): | |
| Faces.append(Fce) | |
| return Faces | |
| # given a part, face and edge of the face this returns the other face | |
| # that shares the same edge | |
| def GetOtherFace(Prt, Edg, TopFace): | |
| Fces = GetFacesFromEdge(Prt, Edg) | |
| for EgeFace in Fces: | |
| if EgeFace.Name != TopFace.Name: | |
| return EgeFace | |
| return None | |
| # creates a pocket hole | |
| def CreatePocketHole(Values): | |
| # TargetEdge = edge that the pocket hole is on | |
| # Fce = face where the pocket is inserted | |
| # DistanceFromEdge = distance from the edge of the face for the packet | |
| # Depth = distance from pocket to drill hole | |
| # Diameter = diameter of packet | |
| # DrillDiameter = diameter of drill hole | |
| # Angle = angle of pocket | |
| TargetEdge = Values[1] | |
| Fce = Values[2] | |
| DistanceFromEdge = Values[3] | |
| Depth = Values[4] | |
| Diameter = Values[5] | |
| DrillDiameter = Values[6] | |
| Angle = Values[7] | |
| Prt = Fce.GetPart() | |
| # get face that has exit hole | |
| ExitFace = GetOtherFace(Prt, TargetEdge, Fce) | |
| # get thickness of part (height of face with exit hole) | |
| ExitFaceEdges = ExitFace.GetEdges() | |
| Thickness = 0 | |
| for ExEdg in ExitFaceEdges: | |
| if ExEdg.Length > 0: | |
| if ExEdg.Length != TargetEdge.Length: | |
| Thickness = ExEdg.Length | |
| if Thickness == 0: | |
| print "Unable to get thickness of part" | |
| sys.exit() | |
| # get location of center of exit hole | |
| ExitHoleCenterX = TargetEdge.Length - DistanceFromEdge | |
| ExitHoleCenterY = Thickness / 2.0 | |
| # get location of exit hole center in 3D coordinates | |
| ExitSk = Prt.AddSketch('Exit Sk', ExitFace) | |
| ExitSk.StartFaceMapping(TargetEdge.Vertices[0], TargetEdge.Vertices[1]) | |
| ExitPt = ExitSk.AddPoint(ExitHoleCenterX, ExitHoleCenterY, False) | |
| ExitSk.StopFaceMapping() | |
| ExitPtGlobal = ExitSk.PointtoGlobal(ExitSk.Figures[0].X, ExitSk.Figures[0].Y) | |
| Prt.RemoveSketch(ExitSk) | |
| # create exit point | |
| ExitPoint = Prt.AddPoint('Exit', ExitPtGlobal[0], ExitPtGlobal[1], ExitPtGlobal[2]) | |
| # get location of entry hole in 2D | |
| EntryHoleCenterX = DistanceFromEdge | |
| EntryHoleCenterY = (Thickness / 2.0) / tan(radians(Angle)) | |
| # get location of entry hole center in 3D coordinates | |
| EntrySk = Prt.AddSketch('Entry Sk', Fce) | |
| EntrySk.StartFaceMapping(TargetEdge.Vertices[0], TargetEdge.Vertices[1]) | |
| EntryPt = EntrySk.AddPoint(EntryHoleCenterX, EntryHoleCenterY, False) | |
| EntrySk.StopFaceMapping() | |
| EntryPtGlobal = EntrySk.PointtoGlobal(EntrySk.Figures[0].X, EntrySk.Figures[0].Y) | |
| Prt.RemoveSketch(EntrySk) | |
| # create entry point | |
| EntryPoint = Prt.AddPoint('Entry', EntryPtGlobal[0], EntryPtGlobal[1], EntryPtGlobal[2]) | |
| # create axis from entry to exit point | |
| PocketAxis = Prt.AddAxis('Pocket Axis', EntryPoint.GetCoordinates(), ExitPoint.GetCoordinates()) | |
| # create plane perpendicular to axis on the start point | |
| nx = ExitPtGlobal[0] - EntryPtGlobal[0] | |
| ny = ExitPtGlobal[1] - EntryPtGlobal[1] | |
| nz = ExitPtGlobal[2] - EntryPtGlobal[2] | |
| EntryPlane = Prt.AddPlane('Entry Plane', [nx, ny, nz], EntryPoint.GetCoordinates()) | |
| # get drill distances | |
| EntrytoExitDistance = (Thickness / 2.0) / sin(radians(Angle)) | |
| Drill1Distance = EntrytoExitDistance - Depth | |
| # first drill | |
| Drill1Sk = Prt.AddSketch('Drill 1', EntryPlane) | |
| DrillCenter = Drill1Sk.GlobaltoPoint(EntryPtGlobal[0], EntryPtGlobal[1], EntryPtGlobal[2]) | |
| Drill1Sk.AddCircle(DrillCenter[0], DrillCenter[1], Diameter, False) | |
| Prt.AddExtrudeCut('Drill 1', Drill1Sk, Drill1Distance * 2, False, Part.EndCondition.MidPlane, None, 0, Part.DirectionType.Normal, None, 0, 0) | |
| # second drill | |
| Drill2Sk = Prt.AddSketch('Drill 2', EntryPlane) | |
| DrillCenter = Drill2Sk.GlobaltoPoint(EntryPtGlobal[0], EntryPtGlobal[1], EntryPtGlobal[2]) | |
| Drill2Sk.AddCircle(DrillCenter[0], DrillCenter[1], DrillDiameter, False) | |
| Prt.AddExtrudeCut('Drill 2', Drill2Sk, 0, False, Part.EndCondition.ThroughAll, None, 0, Part.DirectionType.Normal, None, 0, 0) | |
| # clean up | |
| EntryPoint.Hide() | |
| ExitPoint.Hide() | |
| PocketAxis.Hide() | |
| EntryPlane.Hide() | |
| ########################################################################################### | |
| # check minimum requirements | |
| if AlibreScriptVersion < 1110: | |
| sys.exit('Please upgrade your copy of Alibre Design to use this script') | |
| ScriptName = 'Pocket Hole Creator' | |
| Win = Windows() | |
| # define options to show in dialog window | |
| Options = [] | |
| Options.append([None, WindowsInputTypes.Image, 'PocketHoleCreatorIcon.png', 200]) | |
| Options.append(['Edge', WindowsInputTypes.Edge, None]) | |
| Options.append(['Face', WindowsInputTypes.Face, None]) | |
| Options.append(['Distance From Edge', WindowsInputTypes.Real, 20.0]) | |
| Options.append(['Depth', WindowsInputTypes.Real, 10.0]) | |
| Options.append(['Diameter', WindowsInputTypes.Real, 10.0]) | |
| Options.append(['Drill Diameter', WindowsInputTypes.Real, 4.0]) | |
| Options.append(['Angle', WindowsInputTypes.Real, 15.0]) | |
| # show utility window | |
| Win.UtilityDialog(ScriptName, 'Create Pocket Hole', CreatePocketHole, None, Options, 200) |