Last active
December 21, 2022 17:40
-
-
Save mwganson/09d70aa11183e57b5f5925169b4c70b9 to your computer and use it in GitHub Desktop.
Macro Joint -- FreeCAD macro for making various types of joints
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
__version__ = "0.2022.12.21" | |
#__version__ = "0.2022.12.21" | |
#__title__ = "Joint Macro" | |
#__author__ = "<TheMarkster> 2021" | |
#__license__ = "LGPL 2.1" | |
#__doc__ = "Create a mortise/tenon joint, box joint, dovetail joint, ball joint, or split joint." | |
#__usage__ = '''Select the face and activate the tool, modify properties as desired''' | |
#import Part, FreeCADGui, FreeCAD | |
#from PySide import QtGui,QtCore | |
#import DraftGeomUtils as DGU | |
#import math | |
# | |
#class Joint: | |
# def __init__(self,obj): | |
# jointGroup = "Joint v."+__version__ | |
## obj.addProperty("App::PropertyString","AddSubType",jointGroup,"Additive, Subtractive type when using Part Design pattern tools -- set at time of object creation.") | |
## obj.setEditorMode("AddSubType",1)#readonly | |
# obj.addProperty("App::PropertyFloat", "Undercut","Cantilever","Cantilever undercut (readonly) calculated value") | |
# obj.setEditorMode("Undercut",1) #readonly | |
# obj.addProperty("App::PropertyFloatConstraint","SplitRadius","Split Joint","Radius of spheres").SplitRadius = (1,0.01,float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","SplitSlotWidth","Split Joint","Width of slot in Split Joints").SplitSlotWidth = (2,0,float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","SplitSlotFilletRadius","Split Joint","Radius of fillets at base of slot in Split Joints").SplitSlotFilletRadius = (.5,0.01,float("inf"),.1) | |
# obj.addProperty("App::PropertyBool","SplitRounded","Split Joint", "Split Joints -- Whether to have a rounded end or a flat end").SplitRounded = True | |
# obj.addProperty("App::PropertyFloatConstraint","BallRadius","Ball Joint","Ball joints only -- radius of the ball, in mm").BallRadius=(5,0,1e13,.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallStemRadius","Ball Joint", "Ball joints only -- radius of stem connecting ball to face surface").BallStemRadius=(2,0,1e13,.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallStemLength","Ball Joint", "Ball joints only -- length of stem connecting ball to face surface").BallStemLength=(2,0,1e13,.1) | |
# obj.addProperty("App::PropertyIntegerConstraint","BallSlotCount","Ball Joint","Ball joints only -- count of slots cut from ball").BallSlotCount = (3,0,1000,1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallSlotThickness","Ball Joint","Ball joints only -- size of slot in mm").BallSlotThickness = (.5,0.0,float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallFilletRadius","Ball Joint","Ball joints only -- radius of fillet (in mm) at stem base and ball base").BallFilletRadius = (0.5,0,1e13,.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallSlotToolFilletRadius","Ball Joint","Ball joints only -- radius of fillet (in mm) at stem base and ball base").BallSlotToolFilletRadius = (0.1,0,1e13,.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallSlotToolCoreRadius","Ball Joint","Ball joints only -- radius of slot cutting tool core cylinder").BallSlotToolCoreRadius = (0.5,0,1e13,.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallSlotToolCoreSphereRadius","Ball Joint","Ball joints only -- radius of slot cutting tool core sphere").BallSlotToolCoreSphereRadius = (3.5,0,1e13,.1) | |
# obj.addProperty("App::PropertyFloatConstraint","BallMateStart","Ball Joint","Ball joints only -- ratio from 0 to 1.0 for start of ball mate relative to ball diameter").BallMateStart = (0.33,0.01,1,.01) | |
# obj.addProperty("App::PropertyFloatConstraint","BallSlotStart","Ball Joint","Ball joints only -- ratio from 0 to 1.0 for start of ball slot relative to ball diameter").BallSlotStart = (0.2,0,1,.01) | |
# obj.addProperty("App::PropertyFloatConstraint","RadiusFactor","Cantilever","Radius factor * length = radius of filet at base").RadiusFactor =(0.6,-float("inf"),float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","HeadSpaceAdjust","Cantilever","(Cantilever Mate only) adjusts the amount of free space between the hook nose tip and the mate back wall").HeadSpaceAdjust =(0,-float("inf"),float("inf"),.1) | |
# obj.addProperty("App::PropertyFloat","UndercutAngle","Cantilever","(Cantilver only) adjust angle of undercut") | |
# obj.addProperty("App::PropertyFloatConstraint","UndercutPositionTweak","Cantilever","(Cantilever only) adjust position of undercut").UndercutPositionTweak = (0,-float("inf"),float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","SlotBottom","Annular","Annular Hook only - position of bottom of slot as ratio of overall height, default = 0.25").SlotBottom = (0.25,0,1,.05) | |
# obj.addProperty("App::PropertyFloatConstraint","SlotToolFilletRadius","Annular","Annular Hook only - radius if fillets applied to slot cutting tool, default = .5").SlotToolFilletRadius = (.5,0,float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","Scale",jointGroup,"Scale factor, default = 1.0 - be advised: what works at one scale might not work so well at another").Scale = (1.0,-float("inf"),float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","SlotAngle","Annular","Annular Hook only - angle of slot cutting tool, e.g. 90 degrees would cut 1/4 of the way around").SlotAngle = (20,-360,360,1) | |
# obj.addProperty("App::PropertyFloatConstraint","DiameterTweak","Annular","Annular types only - tweak diameter of revolved faces").DiameterTweak = (0,-float("inf"),float("inf"),.1) | |
# obj.addProperty("App::PropertyIntegerConstraint","SlotCount","Annular","Annular Hook only - number of slots to cut").SlotCount = (6,0,100,1) | |
# obj.addProperty("App::PropertyFloatConstraint","Taper","Cantilever","(Cantilever Hook) thickness of hook end compared to hook base, as a ratio. default: 0.3").Taper = (0.3,0.01,1.0,0.01) | |
# obj.addProperty("App::PropertyFloat","NoseAngle","Cantilever","Angle of cantilever hook nose, in degrees").NoseAngle = 60 | |
# obj.addProperty("App::PropertyFloatConstraint","MaterialDeflection","Cantilever","Permissible deflection of material, default = 2%").MaterialDeflection = (0.02,-float("inf"),float("inf"),.001) | |
# obj.addProperty("App::PropertyFloatConstraint","CantileverMateClearance","Cantilever","Clearance for hook / mate. Default 0.5 mm (for FDM), recommended 0.3 mm for other 3D printer types").CantileverMateClearance = (.5,-float("inf"),float("inf"),.1) | |
# obj.addProperty("App::PropertyFloatConstraint","ShapeDeflection","Cantilever","Deflection factor for rectangular cross section cantilevers, default = 1.09").ShapeDeflection = (1.09,-float("inf"),float("inf"),.01) | |
# obj.addProperty("App::PropertyEnumeration","JointType",jointGroup,"Type of joint to make").JointType=["Mortise","Tenon","Box Joint","Dovetail Joint","Cantilever Hook", "Cantilever Mate","Annular Hook", "Annular Mate","Ball Joint", "Ball Mate","Split Joint", "Split Mate"] | |
# obj.addProperty("App::PropertyFloat","Depth","Dimensions","depth of joint below or above surface").Depth=5 | |
# obj.addProperty("App::PropertyBool","Rounded",jointGroup,"Mortise/Tenon has rounded ends").Rounded=True | |
# obj.addProperty("App::PropertyVector","Position","Positioning","XYZ position adjustments (internal coordinate system)") | |
# obj.addProperty("App::PropertyFloat","FingerWidth","Dimensions","Width of fingers (for Box Joints / Dovetail Joints only)").FingerWidth=5 | |
# obj.addProperty("App::PropertyFloat","FingerAngle","Dimensions","Angle of fingers in degrees (for Box Joints / Dovetail Joints only)").FingerAngle=30 | |
# obj.addProperty("App::PropertyFloat","FingerAngle2","Dimensions","Angle of fingers in degrees (for Box Joints / Dovetail Joints only)").FingerAngle2=30 | |
# obj.addProperty("App::PropertyFloat","BottomFilletAngle","Cantilever","(Cantilever Hook) angle where fillet meets angled edge of hook").BottomFilletAngle = 110 | |
# obj.addProperty("App::PropertyFloatConstraint","Width","Dimensions","width of joint").Width=(5,0.01,10e13,1) | |
# obj.addProperty("App::PropertyFloatConstraint","Length","Dimensions","length of joint from tip of arc to tip of arc").Length=(5,1,10e13,1) | |
# obj.addProperty("App::PropertyFloatConstraint","Offset","Dimensions","Clearance offset for adjusting the fit").Offset=(0,-10e13,10e13,.1) | |
# obj.addProperty("App::PropertyLinkSub","Face",jointGroup,"Selected face for the joint") | |
# obj.addProperty("App::PropertyBool","EditFace",jointGroup,"Trigger, toggle to bring up face editor").EditFace = False | |
# obj.addProperty("App::PropertyBool","Boolean",jointGroup,"Whether to fuse with or cut from base feature").Boolean = True | |
# obj.addProperty("App::PropertyBool","Symmetric","Positioning","Symmetric to plane (at depth in both directions)").Symmetric = True | |
# obj.addProperty("App::PropertyBool","Reversed","Positioning","Extrude in oppositie direction if True").Reversed = False | |
# obj.addProperty("App::PropertyBool","UseOdd","Positioning","(Box Joints / Dovetails only) cut odd fingers if True, else cut even fingers").UseOdd = False | |
# obj.addProperty("App::PropertyBool","ShowTool","Positioning","To aid in positioning the cutting tool set this temporarily to True\nSet back to False after positioning tool to create the joint.").ShowTool = False | |
# obj.addProperty("App::PropertyFloatConstraint","Angle","Positioning","Rotation angle (in degrees) about the local Z axis").Angle = (0,-360,360,1) | |
# obj.addProperty("App::PropertyFloatConstraint","AngleX","Positioning","Rotation angle (in degrees) about the local X axis").AngleX = (0,-360,360,1) | |
# obj.addProperty("App::PropertyFloatConstraint","AngleY","Positioning","Rotation angle (in degrees) about the local Y axis").AngleY = (0,-360,360,1) | |
# if not hasattr(obj,"Refine"): | |
# obj.addProperty("App::PropertyBool","Refine", jointGroup, "refine feature").Refine = False | |
# obj.addProperty("App::PropertyBool","ClaimChildren",jointGroup,"whether to claim children in the tree").ClaimChildren = True | |
# obj.setEditorMode("Placement",2) #hidden | |
# self.editingMode = False | |
# self.fpName = obj.Name | |
# obj.Proxy = self | |
# | |
# def recompute(self): | |
# t = QtCore.QTimer() | |
# t.singleShot(50,FreeCAD.ActiveDocument.recompute) | |
# | |
# def onBeforeChange(self,fp,prop): | |
# pass | |
# | |
# def onChanged(self,fp,prop): | |
# if prop == "EditFace" and fp.EditFace == True: | |
# t = QtCore.QTimer() | |
# t.singleShot(50, self.editFace) #avoid warning message about selection changing while committing data | |
# fp.EditFace = False | |
# | |
# def editFace(self): | |
# fp = FreeCAD.ActiveDocument.getObject(self.fpName) | |
# if not fp.Face: | |
# return | |
# object = fp.Face[0] | |
# if not object: | |
# return | |
# if not FreeCADGui.Control.activeDialog(): | |
# panel = TaskEditLinkSubPanel(fp,"Face","Face") | |
# FreeCADGui.Control.showDialog(panel) | |
# self.editingMode = True #tells execute() not to hide the linked object | |
# else: | |
# self.editingMode=False | |
# FreeCAD.Console.PrintError("Another task dialog is active. Close that one and try again.\n") | |
# | |
# def hideThem(self,fp,proplist,hide=True): | |
# for prop in proplist: | |
# mode = 2 if hide else 0 | |
# fp.setEditorMode(prop,mode) | |
# | |
# def hideProps(self,fp): | |
# '''hide properties unrelated to this type of joint''' | |
# mortiseTenonOnly = ["Rounded"] | |
# splitProps = ["SplitRadius","SplitSlotWidth","SplitSlotFilletRadius","SplitRounded"] | |
# fingerProps = ["FingerAngle","FingerAngle2","FingerWidth","UseOdd",] | |
# dovetailProps = ["FingerAngle","FingerAngle2"] | |
# cantileverProps = ["Taper","UndercutAngle","UndercutPositionTweak","HeadSpaceAdjust","BottomFilletAngle","CantileverMateClearance","MaterialDeflection","NoseAngle","RadiusFactor","ShapeDeflection","Undercut"] | |
# notLatchProps = ["Offset"] | |
# annularProps = ["SlotToolFilletRadius","DiameterTweak","SlotAngle","SlotBottom","SlotCount"] | |
# notAnnularProps = ["Width"] | |
# notBallProps = ["Width","Length","Depth"] | |
# ballProps = ["BallSlotToolCoreRadius","BallSlotToolCoreSphereRadius","BallRadius","BallStemRadius","BallStemLength","BallSlotCount","BallSlotThickness","BallFilletRadius","BallSlotToolFilletRadius","BallMateStart","BallSlotStart"] | |
# if fp.JointType in ["Mortise","Tenon"]: | |
# self.hideThem(fp,mortiseTenonOnly,False) | |
# self.hideThem(fp,splitProps, True) | |
# self.hideThem(fp,notBallProps,False) | |
# self.hideThem(fp,ballProps,True) | |
# self.hideThem(fp,notAnnularProps,False) | |
# self.hideThem(fp,annularProps,True) | |
# self.hideThem(fp,fingerProps,True) | |
# self.hideThem(fp,cantileverProps, True) | |
# self.hideThem(fp,notLatchProps,False) | |
# elif "Split" in fp.JointType: | |
# self.hideThem(fp,mortiseTenonOnly,True) | |
# self.hideThem(fp,splitProps, False) | |
# self.hideThem(fp,notBallProps,False) | |
# self.hideThem(fp,ballProps,True) | |
# self.hideThem(fp,notAnnularProps,False) | |
# self.hideThem(fp,annularProps,True) | |
# self.hideThem(fp,fingerProps,True) | |
# self.hideThem(fp,dovetailProps,True) | |
# self.hideThem(fp,cantileverProps, True) | |
# self.hideThem(fp,notLatchProps,False) | |
# elif fp.JointType == "Box Joint": | |
# self.hideThem(fp,mortiseTenonOnly,True) | |
# self.hideThem(fp,splitProps, True) | |
# self.hideThem(fp,notBallProps,False) | |
# self.hideThem(fp,ballProps,True) | |
# self.hideThem(fp,notAnnularProps,False) | |
# self.hideThem(fp,annularProps,True) | |
# self.hideThem(fp,fingerProps,False) | |
# self.hideThem(fp,dovetailProps,True) | |
# self.hideThem(fp,cantileverProps, True) | |
# self.hideThem(fp,notLatchProps,False) | |
# elif fp.JointType == "Dovetail Joint": | |
# self.hideThem(fp,mortiseTenonOnly,True) | |
# self.hideThem(fp,splitProps, True) | |
# self.hideThem(fp,notBallProps,False) | |
# self.hideThem(fp,ballProps,True) | |
# self.hideThem(fp,notAnnularProps,False) | |
# self.hideThem(fp,annularProps,True) | |
# self.hideThem(fp,fingerProps,False) | |
# self.hideThem(fp,dovetailProps,False) | |
# self.hideThem(fp,cantileverProps, True) | |
# self.hideThem(fp,notLatchProps,False) | |
# elif "Cantilever" in fp.JointType: | |
# self.hideThem(fp,mortiseTenonOnly,True) | |
# self.hideThem(fp,splitProps, True) | |
# self.hideThem(fp,notBallProps,False) | |
# self.hideThem(fp,ballProps,True) | |
# self.hideThem(fp,notAnnularProps,False) | |
# self.hideThem(fp,annularProps,True) | |
# self.hideThem(fp,fingerProps,True) | |
# self.hideThem(fp,dovetailProps,True) | |
# self.hideThem(fp,cantileverProps, False) | |
# self.hideThem(fp,notLatchProps,True) | |
# elif "Annular" in fp.JointType: | |
# self.hideThem(fp,mortiseTenonOnly,True) | |
# self.hideThem(fp,splitProps, True) | |
# self.hideThem(fp,notBallProps,False) | |
# self.hideThem(fp,ballProps,True) | |
# self.hideThem(fp,notAnnularProps,True) | |
# self.hideThem(fp,annularProps,False) | |
# self.hideThem(fp,fingerProps,True) | |
# self.hideThem(fp,dovetailProps,True) | |
# self.hideThem(fp,cantileverProps, False) | |
# self.hideThem(fp,notLatchProps,True) | |
# elif "Ball" in fp.JointType: | |
# self.hideThem(fp,mortiseTenonOnly,True) | |
# self.hideThem(fp,splitProps, True) | |
# self.hideThem(fp,notBallProps,True) | |
# self.hideThem(fp,ballProps,False) | |
# self.hideThem(fp,annularProps,True) | |
# self.hideThem(fp,fingerProps,True) | |
# self.hideThem(fp,dovetailProps,True) | |
# self.hideThem(fp,cantileverProps, True) | |
# self.hideThem(fp,notLatchProps,True) | |
# | |
# def roundIt(self, x): | |
# return round(x, 12) | |
# | |
# def execute(self,fp): | |
# if not fp.Face: | |
# return | |
# self.hideProps(fp) | |
# fp.Label2 = "("+fp.JointType+")" | |
# object = fp.Face[0] | |
# if not object: | |
# return | |
# face = object.getSubObject(fp.Face[1][0]) | |
# if fp.JointType == "Mortise" or fp.JointType == "Tenon": | |
# shape = self.makeMortiseTenon(fp) | |
# elif fp.JointType == "Box Joint": | |
# shape = self.makeBoxJoint(fp) | |
# elif fp.JointType == "Dovetail Joint": | |
# shape = self.makeDovetailJoint(fp) | |
# elif fp.JointType == "Cantilever Hook": | |
# shape = self.makeCantileverHook(fp) | |
# elif fp.JointType == "Cantilever Mate": | |
# shape = self.makeCantileverMate(fp) | |
# elif fp.JointType == "Annular Hook": | |
# shape = self.makeAnnularHook(fp) | |
# elif fp.JointType == "Annular Mate": | |
# shape = self.makeAnnularMate(fp) | |
# elif fp.JointType == "Ball Joint": | |
# shape = self.makeBallJoint(fp) | |
# elif fp.JointType == "Ball Mate": | |
# shape = self.makeBallMate(fp) | |
# elif fp.JointType == "Split Joint": | |
# shape = self.makeSplitJoint(fp) | |
# elif fp.JointType == "Split Mate": | |
# shape = self.makeSplitJoint(fp,True) | |
# else: | |
# FreeCAD.Console.PrintError(f"Unsupported joint type: {fp.JointType}\n") | |
# return | |
# shape = shape.scale(fp.Scale) if fp.Scale != 1.0 else shape | |
# x,y,z = map(self.roundIt, face.Surface.Axis) #round x,y,z to 12 digits | |
## thanks to openBrain on the FreeCAD forum for this next line of code | |
# shape.Placement = fp.Placement.multiply(FreeCAD.Placement(face.BoundBox.Center, FreeCAD.Rotation(FreeCAD.Vector(0,0,1),FreeCAD.Vector(x,y,z)))) | |
# if fp.JointType == "Tenon" or fp.ShowTool or fp.JointType == "Cantilever Hook" or fp.JointType == "Annular Hook" or fp.JointType == "Ball Joint" or fp.JointType == "Split Joint": | |
# full_shape = object.Shape.fuse(shape) if fp.Boolean else shape | |
# else: #other types all require cuts | |
# full_shape = object.Shape.cut(shape) if fp.Boolean else shape | |
# fp.Shape = full_shape.removeSplitter() if fp.Refine else full_shape | |
# if hasattr(fp,"BaseFeature") and hasattr(fp,"AddSubShape"): | |
# if fp.BaseFeature and not shape.isNull(): | |
# fp.AddSubShape = shape.transformShape(fp.Placement.inverse().toMatrix(),True) | |
# else: | |
# fp.AddSubShape = shape | |
# if not self.editingMode: | |
# object.ViewObject.Visibility = False if shape and fp.Boolean else True | |
# | |
# def makeSplitJoint(self,fp,makeMate = False): | |
# ##Length is also the diameter of the rounded nose | |
# width = fp.Width + fp.Offset | |
# length = fp.Length + fp.Offset | |
# depth = fp.Depth + fp.Offset | |
# noseRadius = length/2 | |
# splitRadius = fp.SplitRadius + fp.Offset | |
# splitSlotFilletRadius = fp.SplitSlotFilletRadius + fp.Offset | |
# ## points | |
# bottomFrontLeft = FreeCAD.Vector(-width/2, -length/2, 0) | |
# topFrontLeft = FreeCAD.Vector(-width/2, -length/2, depth) | |
# topBackLeft = FreeCAD.Vector(-width/2,length/2, depth) | |
# bottomBackLeft = FreeCAD.Vector(-width/2, length/2, 0) | |
# ballCenterLeft = FreeCAD.Vector(-width/2, 0, depth - noseRadius) | |
# frontArcLeft = FreeCAD.Vector(-width/2, -length/2, depth - noseRadius) | |
# topArcLeft = FreeCAD.Vector(-width/2, 0, depth) | |
# backArcLeft = FreeCAD.Vector(-width/2, length/2, depth - noseRadius) | |
# if fp.SplitRounded: | |
# frontBottomLine = Part.makeLine(bottomFrontLeft, frontArcLeft) | |
# arc = Part.ArcOfCircle(frontArcLeft, topArcLeft, backArcLeft).toShape() | |
# backBottomLine = Part.makeLine(backArcLeft, bottomBackLeft) | |
# bottomLine = Part.makeLine(bottomBackLeft, bottomFrontLeft) | |
# wire = Part.Wire([frontBottomLine, arc, backBottomLine, bottomLine]) | |
# else: | |
# frontLine = Part.makeLine(bottomFrontLeft, topFrontLeft) | |
# topLine = Part.makeLine(topFrontLeft, topBackLeft) | |
# backLine = Part.makeLine(topBackLeft, bottomBackLeft) | |
# bottomLine = Part.makeLine(bottomBackLeft, bottomFrontLeft) | |
# wire = Part.Wire([frontLine, topLine, backLine, bottomLine]) | |
# face = Part.makeFace(wire, "Part::FaceMakerCheese") | |
# extrude = face.extrude(FreeCAD.Vector(1,0,0)*width/2) | |
# if not makeMate and fp.SplitSlotWidth != 0: | |
# extrude = face.extrude(FreeCAD.Vector(1,0,0)*(width/2 - fp.SplitSlotWidth/2)) | |
# sphere = Part.makeSphere(splitRadius, ballCenterLeft, FreeCAD.Vector(1,0,0),0,90,360) | |
# sphere.Placement.rotate(ballCenterLeft, FreeCAD.Vector(0,-1,0),180) | |
# extrude = extrude.fuse(sphere) | |
# filletTop = extrude.Vertex2.Point.add(FreeCAD.Vector(0,0,splitSlotFilletRadius)) | |
# filletMiddle = extrude.Vertex2.Point.add(FreeCAD.Vector(splitSlotFilletRadius, 0, splitSlotFilletRadius)) | |
# filletBottom = extrude.Vertex2.Point.add(FreeCAD.Vector(splitSlotFilletRadius, 0, 0)) | |
# filletArc = Part.makeCircle(splitSlotFilletRadius, filletMiddle, FreeCAD.Vector(0,1,0),90,180) | |
# filletBottomLine = Part.makeLine(filletBottom, extrude.Vertex2.Point) | |
# filletLeftLine = Part.makeLine(extrude.Vertex2.Point, filletTop) | |
# filletWire = Part.Wire([filletLeftLine,filletArc,filletBottomLine]) | |
# filletFace = Part.makeFace(filletWire,"Part::FaceMakerCheese") | |
# fillet = filletFace.extrude(FreeCAD.Vector(0,1,0)*length) | |
# mid = extrude.Edge1.CenterOfMass | |
# if not makeMate and fp.SplitSlotWidth != 0: | |
# fillet = fillet.fuse(fillet.mirror(mid,FreeCAD.Vector(1,0,0))) | |
# else: | |
# fillet = fillet.mirror(mid,FreeCAD.Vector(1,0,0)) | |
# extrude = extrude.fuse(fillet) | |
# extrude = extrude.fuse(extrude.mirror(FreeCAD.Vector(0,0,0), FreeCAD.Vector(1,0,0))) | |
# mirror = extrude.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1)) | |
# if fp.Symmetric: | |
# fusion = extrude.fuse(mirror) | |
# elif fp.Reversed: | |
# fusion = mirror | |
# else: | |
# fusion = extrude | |
# cog = FreeCAD.Vector(0,0,0) | |
# fusion.rotate(cog,FreeCAD.Vector(0,0,1),fp.Angle) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# fusion.rotate(cog,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# fusion.rotate(cog,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# fusion.Placement.move(fp.Position) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# return fusion | |
# | |
# def makeBoxJoint(self,fp): | |
# '''make the box joint shape''' | |
# num = fp.Width / (fp.FingerWidth) | |
# num = int(math.ceil(num)) | |
# boxes = [] | |
# depth = fp.Depth if not fp.Symmetric else fp.Depth*2 | |
# for n in range(0,num): | |
# if bool(n % 2 == 0 and fp.UseOdd) or bool(n % 2 == 1 and not fp.UseOdd): | |
# continue | |
# x = -fp.Offset -fp.Width/2 + n * fp.FingerWidth | |
# y = -fp.Offset -fp.Length/2 | |
# z = 0 if not fp.Symmetric else -fp.Depth | |
# z = -fp.Depth if fp.Reversed else z | |
# pt = FreeCAD.Vector(x,y,z).add(fp.Position) | |
# boxes.append(Part.makeBox(fp.FingerWidth+fp.Offset*2, fp.Length+fp.Offset, depth+fp.Offset*2,pt-FreeCAD.Vector(0,0,fp.Offset*2))) | |
# fusion = boxes[0].fuse(boxes[1:]) if len(boxes) > 1 else boxes[0] | |
# fusion.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),fp.Angle) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.rotate(FreeCAD.Vector(0,0,-fp.Depth/2),FreeCAD.Vector(0,1,0),fp.AngleY) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.rotate(FreeCAD.Vector(0,0,-fp.Depth/2),FreeCAD.Vector(1,0,0),fp.AngleX) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# return fusion | |
# | |
# def makeWedge(self, fp, wid, length, ht, pt): | |
# '''support 2 angles for compound dovetails''' | |
# tanht = math.tan(math.radians(-fp.FingerAngle/2)) * ht / 2 | |
# tanlength = math.tan(math.radians(-fp.FingerAngle2/2)) * length / 2 | |
# frontLeftBottom = FreeCAD.Vector(-tanht,0,0) | |
# frontLeftTop = FreeCAD.Vector(tanht,0, ht) | |
# frontLeft = Part.makeLine(frontLeftBottom, frontLeftTop) | |
# topLeftBack = FreeCAD.Vector(tanht + tanlength, length, ht) | |
# topLeft = Part.makeLine(frontLeftTop, topLeftBack) | |
# topLeftDirection = topLeft.Vertex2.Point.sub(topLeft.Vertex1.Point) | |
# backLeftBottom = frontLeftBottom.add(topLeftDirection) | |
# backLeft = Part.makeLine(topLeftBack,backLeftBottom) | |
# bottomLeft = Part.makeLine(frontLeftBottom, backLeftBottom) | |
# leftWire = Part.Wire([bottomLeft,backLeft,topLeft,frontLeft]) | |
# leftFace = Part.makeFace(leftWire,"Part::FaceMakerCheese") | |
# mirror = leftFace.mirror(FreeCAD.Vector(wid/2+leftFace.CenterOfMass.x,0,0),FreeCAD.Vector(1,0,0)) | |
# loft = Part.makeLoft([leftWire,mirror.Wire1],True) | |
# loft.Placement.move(pt) | |
# symmetric_mirror = loft.mirror(pt,FreeCAD.Vector(0,0,1)) | |
# if fp.Symmetric: | |
# return loft.fuse(symmetric_mirror) | |
# if not fp.Reversed: | |
# return symmetric_mirror | |
# else: | |
# return loft | |
# | |
# def makeDovetailJoint(self,fp): | |
# '''make the dovetial joint shape''' | |
# num = fp.Width / (fp.FingerWidth) | |
# num = int(math.ceil(num)) | |
# wedges = [] | |
# for n in range(-1,num+1): | |
# if bool(n % 2 == 0 and fp.UseOdd) or bool(n % 2 == 1 and not fp.UseOdd): | |
# continue | |
# x = -fp.Offset -fp.Width/2 + n * fp.FingerWidth | |
# y = -fp.Offset -fp.Length/2 | |
# z = 0 | |
# pt = FreeCAD.Vector(x,y,z).add(fp.Position) | |
# wedges.append(self.makeWedge(fp, fp.FingerWidth+fp.Offset*2, fp.Length+fp.Offset, fp.Depth+fp.Offset,pt)) | |
# fusion = wedges[0].fuse(wedges[1:]) if len(wedges) > 1 else wedges[0] | |
# fusion.rotate(FreeCAD.Vector(0,0,-fp.Depth/2),FreeCAD.Vector(0,0,1),fp.Angle) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.rotate(FreeCAD.Vector(0,0,-fp.Depth/2),FreeCAD.Vector(0,1,0),fp.AngleY) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.rotate(FreeCAD.Vector(0,0,-fp.Depth/2),FreeCAD.Vector(1,0,0),fp.AngleX) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# return fusion | |
# | |
# def makeCantileverHookFace(self,fp): | |
# length = fp.Length #h in diagram | |
# depth = fp.Depth #length in diagram | |
# angle = fp.NoseAngle | |
# radius = fp.RadiusFactor * length #default = 0.6 * length | |
# undercut = fp.ShapeDeflection * fp.MaterialDeflection * depth * depth / length | |
# fp.Undercut = undercut | |
# upt = fp.UndercutPositionTweak | |
# uat = math.tan(math.radians(fp.UndercutAngle))*undercut #undercut angle tweak | |
# baseline = length + radius + radius #bottom of bottom fillet to top of top fillet | |
# ## Points | |
# filletCenterBottom = FreeCAD.Vector(radius, -baseline/2, 0) | |
# filletCenterTop = FreeCAD.Vector(radius, baseline/2, 0) | |
# baselineBottom = FreeCAD.Vector(0, -baseline/2, 0) | |
# baselineTop = FreeCAD.Vector(0, baseline/2, 0) | |
# undercutBottom = FreeCAD.Vector(uat + upt + radius + depth, length/2, 0) | |
# undercutTop = FreeCAD.Vector(upt + radius + depth, length/2 + undercut, 0) | |
# undercutRight = FreeCAD.Vector(radius + depth + undercut, length/2 + undercut, 0) | |
# noseBottomY = -length/2 + (1-fp.Taper) * length | |
# noseBottom = FreeCAD.Vector(radius + depth + undercut + math.tan(math.radians(angle))*undercut, noseBottomY, 0) | |
# noseTop = FreeCAD.Vector(radius + depth + undercut + math.tan(math.radians(angle))*undercut, length/2, 0) | |
# ## Edges | |
# bottomArc = Part.makeCircle(radius,filletCenterBottom,FreeCAD.Vector(0,0,1),fp.BottomFilletAngle,180) | |
# topArc = Part.makeCircle(radius,filletCenterTop,FreeCAD.Vector(0,0,1),180,270) | |
# hookBottom = Part.makeLine(bottomArc.Vertex1.Point, noseBottom) | |
# hookNoseBottom = Part.makeLine(noseBottom, noseTop) | |
# hookNoseSlant = Part.makeLine(noseTop, undercutRight) | |
# hookNoseTop = Part.makeLine(undercutRight, undercutTop) | |
# hookNoseLeft = Part.makeLine(undercutTop, undercutBottom) | |
# hookTop = Part.makeLine(undercutBottom, topArc.Vertex2.Point) | |
# hookBaseline = Part.makeLine(topArc.Vertex1.Point,bottomArc.Vertex2.Point) | |
# wire = Part.Wire([hookBaseline, topArc, hookTop, hookNoseLeft, hookNoseTop, hookNoseSlant, hookNoseBottom, hookBottom, bottomArc]) | |
# face = Part.makeFace(wire,"Part::FaceMakerCheese") | |
# return face | |
# | |
# def makeBallJoint(self,fp): | |
# ## points | |
# bottomLeft = FreeCAD.Vector(-fp.BallStemRadius - fp.BallFilletRadius, 0, 0) | |
# arcCenter = FreeCAD.Vector(-fp.BallStemRadius - fp.BallFilletRadius, 0, fp.BallFilletRadius) | |
# filletTop = FreeCAD.Vector(-fp.BallStemRadius,0,fp.BallFilletRadius) | |
# sagitta = fp.BallRadius - math.sqrt(fp.BallRadius**2 - fp.BallStemRadius**2) | |
# ballBottomLeft = FreeCAD.Vector(-fp.BallStemRadius, 0, fp.BallStemLength+sagitta) | |
# ballLeft = FreeCAD.Vector(-fp.BallRadius,0,fp.BallStemLength + fp.BallRadius) | |
# ballTop = FreeCAD.Vector(0,0,fp.BallStemLength + fp.BallRadius + fp.BallRadius) | |
# bottomRight = FreeCAD.Vector(0,0,0) | |
# | |
# ## edges | |
# bottom = Part.makeLine(bottomRight,bottomLeft) | |
# filletArc = Part.makeCircle(fp.BallFilletRadius, arcCenter, FreeCAD.Vector(0,1,0),0, 90) | |
# stemLeft = Part.makeLine(filletTop, ballBottomLeft) | |
# ball = Part.ArcOfCircle(ballBottomLeft,ballLeft,ballTop) | |
# rightSide = Part.makeLine(ballTop,bottomRight) | |
# | |
# wire = Part.Wire([bottom,filletArc,stemLeft,ball.toShape().Edge1,rightSide]) | |
# face = Part.makeFace(wire,"Part::FaceMakerCheese") | |
# revolve = face.revolve(bottomRight, FreeCAD.Vector(0,0,1),180) | |
# revolve = revolve.makeFillet(fp.BallFilletRadius,[revolve.Edge7]) | |
# revolve = revolve.fuse(revolve.mirror(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,1,0))) | |
# ## cut slots in ball | |
# btPos = fp.BallStemLength + fp.BallSlotStart * (2*fp.BallRadius) | |
# thick = fp.BallSlotThickness | |
# centerEdge = Part.makeLine(FreeCAD.Vector(-fp.BallRadius*2,-thick/2,btPos),FreeCAD.Vector(-fp.BallRadius*2,-thick/2,fp.BallStemLength+3*fp.BallRadius)) | |
# topEdge = Part.makeLine(FreeCAD.Vector(-fp.BallRadius*2,-thick/2,fp.BallStemLength+3*fp.BallRadius),FreeCAD.Vector(fp.BallRadius*2,-thick/2,fp.BallStemLength+3*fp.BallRadius)) | |
# rightEdge = Part.makeLine(FreeCAD.Vector(fp.BallRadius*2,-thick/2,fp.BallStemLength+3*fp.BallRadius),FreeCAD.Vector(fp.BallRadius*2,-thick/2,btPos)) | |
# bottomEdge = Part.makeLine(FreeCAD.Vector(-fp.BallRadius*2,-thick/2,btPos),FreeCAD.Vector(fp.BallRadius*2,-thick/2,btPos)) | |
# wire = Part.Wire([centerEdge,topEdge,rightEdge,bottomEdge]) | |
# face = Part.makeFace(wire,"Part::FaceMakerCheese") | |
# slotTool = face.extrude(FreeCAD.Vector(0,thick,0)) | |
# slotTool = slotTool.makeFillet(fp.BallSlotToolFilletRadius,[slotTool.Edge6,slotTool.Edge7,slotTool.Edge11,slotTool.Edge12]) | |
# if fp.BallSlotToolCoreSphereRadius >= fp.BallRadius: | |
# FreeCAD.Console.PrintError("Ball joint core tool sphere radius must be smaller than ball radius.\n") | |
# if not fp.BallSlotToolCoreRadius == 0: | |
# core = Part.makeCylinder(fp.BallSlotToolCoreRadius,fp.BallStemLength+3*fp.BallRadius,FreeCAD.Vector(0,0,btPos-.01)) | |
# coreSphere = Part.makeSphere(fp.BallSlotToolCoreSphereRadius,FreeCAD.Vector(0,0,fp.BallStemLength+fp.BallRadius)) if fp.BallSlotToolCoreSphereRadius != 0 else Part.Shape() | |
# toolCopies = core.fuse(coreSphere) if not coreSphere.isNull() else core | |
# else: | |
# toolCopies = Part.Shape() | |
# for ii in range(0,fp.BallSlotCount): | |
# copy = slotTool.copy() | |
# copy.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1), ii * (180 / fp.BallSlotCount)) | |
# if toolCopies.isNull(): | |
# toolCopies = copy | |
# else: | |
# toolCopies = toolCopies.fuse(copy) | |
# toolCopies = toolCopies.removeSplitter() if not toolCopies.isNull() else toolCopies | |
# if not toolCopies.isNull(): | |
# revolve = revolve.cut(toolCopies) | |
# mirror = revolve.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1)) | |
# if fp.Symmetric: | |
# revolve = revolve.fuse(mirror) | |
# elif fp.Reversed: | |
# revolve = mirror | |
# cog = mirror.Face1.CenterOfGravity | |
# revolve.rotate(cog,FreeCAD.Vector(0,0,1),fp.Angle) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.Placement.move(fp.Position) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# return revolve | |
# | |
# def makeBallMate(self,fp): | |
# #ballProps = ["BallRadius","BallStemRadius","BallStemLength",\ | |
# #"BallSlotCount","BallSlotThickness","BallFilletRadius","BallSlotToolFilletRadius",\ | |
# #"BallMateStart","BallSlotStart"] | |
# sphere = Part.makeSphere(fp.BallRadius, FreeCAD.Vector(0,0,fp.BallStemLength+fp.BallRadius)) | |
# start = fp.BallStemLength + fp.BallMateStart * (fp.BallRadius * 2) | |
# circle = Part.makeCircle(fp.BallRadius, FreeCAD.Vector(0,0,start),FreeCAD.Vector(0,0,1)) | |
# face = Part.makeFace(circle, "Part::FaceMakerCheese") | |
# extrude = face.extrude(FreeCAD.Vector(0,0,-1)) | |
# common = extrude.common(sphere) | |
# r1 = common.Edge1.Curve.Radius | |
# cylinder = Part.makeCylinder(r1,start) | |
# fusion = cylinder.fuse(sphere) | |
# mirror = fusion.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1)) | |
# if fp.Symmetric: | |
# fusion = fusion.fuse(mirror) | |
# elif fp.Reversed: | |
# fusion = mirror | |
# cog = mirror.Face1.CenterOfGravity | |
# fusion.rotate(cog,FreeCAD.Vector(0,0,1),fp.Angle) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# fusion.rotate(cog,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# fusion.rotate(cog,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# fusion.Placement.move(fp.Position) | |
# fusion = fusion.transformShape(fusion.Placement.toMatrix(),True) | |
# fusion.Placement = FreeCAD.Placement() | |
# return fusion | |
# | |
# def makeAnnularHook(self,fp): | |
# face = self.makeCantileverHookFace(fp) | |
# direction = FreeCAD.Vector(1,0,0) | |
# pt = FreeCAD.Vector(0, face.BoundBox.YMin - fp.DiameterTweak, 0) | |
# revolve = face.revolve(pt,direction,180) | |
# revolve = revolve.fuse(revolve.mirror(pt,FreeCAD.Vector(0,0,1))) | |
# revolve.Placement.move(pt*-1) #centers at pt, axis of rotation | |
# revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# #at this point the object is oriented horizontally and centered on the origin | |
# #will be rotated vertically in a later step | |
# wid = revolve.BoundBox.XLength #overall height, once rotated | |
# ht = revolve.BoundBox.ZLength | |
# length = revolve.BoundBox.YLength | |
# btPos = wid * fp.SlotBottom #bottom position of slots to be cut | |
# #fp.SlotAngle will be angle of slots | |
# #fp.SlotCount will be number of slots to cut | |
# centerEdge = Part.makeLine(FreeCAD.Vector(btPos,0,0),FreeCAD.Vector(btPos,length,0)) | |
# topEdge = Part.makeLine(FreeCAD.Vector(btPos,length,0),FreeCAD.Vector(wid,length,0)) | |
# rightEdge = Part.makeLine(FreeCAD.Vector(wid,length,0),FreeCAD.Vector(wid,0,0)) | |
# bottomEdge = Part.makeLine(FreeCAD.Vector(wid,0,0),FreeCAD.Vector(btPos,0,0)) | |
# wire = Part.Wire([centerEdge,topEdge,rightEdge,bottomEdge]) | |
# face = Part.makeFace(wire,"Part::FaceMakerCheese") | |
# slotTool = face.revolve(centerEdge.Vertex1.Point,FreeCAD.Vector(1,0,0),fp.SlotAngle) | |
# slotTool = slotTool.makeFillet(fp.SlotToolFilletRadius,[slotTool.Edge2,slotTool.Edge3]) | |
# toolCopies = Part.Shape() | |
# for ii in range(0,fp.SlotCount): | |
# copy = slotTool.copy() | |
# copy.rotate(centerEdge.Vertex1.Point,FreeCAD.Vector(1,0,0), ii * (360 / fp.SlotCount)) | |
# if toolCopies.isNull(): | |
# toolCopies = copy | |
# else: | |
# toolCopies = toolCopies.fuse(copy) | |
# if not toolCopies.isNull(): | |
# revolve = revolve.cut(toolCopies) | |
# mirror = revolve.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(-1,0,0)) | |
# if fp.Symmetric: | |
# revolve = revolve.fuse(mirror) | |
# elif fp.Reversed: | |
# revolve = mirror | |
# cog = mirror.Face1.CenterOfGravity | |
# revolve.Placement.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,1,0),90) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(0,0,1),fp.Angle) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.Placement.move(fp.Position) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# return revolve | |
# | |
# def makeCantileverHook(self,fp): | |
# face = self.makeCantileverHookFace(fp) | |
# face.Placement.move(fp.Position) | |
# direction = FreeCAD.Vector(0,0,1) | |
# extrude = face.extrude(direction*fp.Width) | |
# if fp.Symmetric or fp.Reversed: | |
# mirror = extrude.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(-1,0,0)) | |
# if fp.Symmetric: | |
# extrude = extrude.fuse(mirror) | |
# elif fp.Reversed: | |
# extrude = mirror | |
# extrude.Placement.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(-1,-1,-1),120) | |
# extrude.Placement.move(FreeCAD.Vector(0,-fp.Width/2,0)) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.Placement = FreeCAD.Placement() | |
# extrude.Placement.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),180) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.Placement = FreeCAD.Placement() | |
# if not fp.Symmetric: | |
# cog = extrude.Face1.CenterOfGravity | |
# else: | |
# cog = extrude.Vertex2.Point.add(extrude.Vertex9.Point)/2 | |
# extrude.rotate(cog,FreeCAD.Vector(0,0,1),fp.Angle) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.rotate(cog,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.rotate(cog,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# return extrude | |
# | |
# def makeAnnularMate(self,fp): | |
# face = self.makeCantileverMateFace(fp,True) | |
# direction = FreeCAD.Vector(1,0,0) | |
# pt = FreeCAD.Vector(0,face.BoundBox.YMin, 0) | |
# revolve = face.revolve(pt,direction,180) | |
# revolve = revolve.fuse(revolve.mirror(pt,FreeCAD.Vector(0,0,1))) | |
# revolve.Placement.move(pt*-1) #centers at pt, axis of rotation | |
# revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# #at this point the object is oriented horizontally and centered on the origin | |
# #will be rotated vertically in a later step | |
# mirror = revolve.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(-1,0,0)) | |
# if fp.Symmetric: | |
# revolve = revolve.fuse(mirror) | |
# elif fp.Reversed: | |
# revolve = mirror | |
# cog = mirror.Face1.CenterOfGravity | |
# revolve.Placement.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,1,0),90) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(0,0,1),fp.Angle) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.rotate(cog,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# revolve.Placement.move(fp.Position) | |
# revolve = revolve.transformShape(revolve.Placement.toMatrix(),True) | |
# revolve.Placement = FreeCAD.Placement() | |
# return revolve | |
# | |
# def makeCantileverMateFace(self,fp, annular=False): | |
# ##cantilever hook calculations first, then we'll use them as references for the | |
# ##mate cutting tool shape, offset by fp.CantileverMateClearance (default 0.5mm) | |
# | |
# length = fp.Length #h in diagram | |
# depth = fp.Depth #length in diagram | |
# angle = fp.NoseAngle | |
# radius = fp.RadiusFactor * length #default = 0.6 * length | |
# undercut = fp.ShapeDeflection * fp.MaterialDeflection * depth * depth / length | |
# fp.Undercut = undercut | |
# baseline = length + radius + radius #bottom of bottom fillet to top of top fillet | |
# upt = fp.UndercutPositionTweak | |
# uat = math.tan(math.radians(fp.UndercutAngle))*undercut #undercut angle tweak | |
# ## Points | |
# filletCenterBottom = FreeCAD.Vector(radius, -baseline/2, 0) | |
# filletCenterTop = FreeCAD.Vector(radius, baseline/2, 0) | |
# baselineBottom = FreeCAD.Vector(0, -baseline/2, 0) | |
# baselineTop = FreeCAD.Vector(0, baseline/2, 0) | |
# undercutBottom = FreeCAD.Vector(upt + uat + radius + depth, length/2, 0) | |
# undercutTop = FreeCAD.Vector(upt + radius + depth, length/2 + undercut, 0) | |
# undercutRight = FreeCAD.Vector(radius + depth + undercut, length/2 + undercut, 0) | |
# noseBottom = FreeCAD.Vector(radius + depth + undercut + math.tan(math.radians(angle))*undercut, 0, 0) | |
# noseTop = FreeCAD.Vector(radius + depth + undercut + math.tan(math.radians(angle))*undercut, length/2, 0) | |
# topArc = Part.makeCircle(radius,filletCenterTop,FreeCAD.Vector(0,0,1),180,270) | |
# | |
# ## now for the mate points | |
# offset = fp.CantileverMateClearance | |
# baselineTop2 = baselineTop.add(FreeCAD.Vector(0,offset,0)) | |
# topArcPt2 = topArc.Vertex2.Point.add(FreeCAD.Vector(0,offset,0)) | |
# undercutBottom2 = undercutBottom.add(FreeCAD.Vector(-offset,offset,0)) | |
# undercutTop2 = undercutTop.add(FreeCAD.Vector(-offset,offset,0)) | |
# topRight = FreeCAD.Vector(noseTop.x + undercut + fp.HeadSpaceAdjust, undercutTop.y + offset,0) | |
# if not annular: | |
# bottomRight = FreeCAD.Vector(topRight.x, -topRight.y - offset, 0) | |
# baselineBottom2 = FreeCAD.Vector(0, -topRight.y -offset, 0) | |
# else: | |
# bottomRight = FreeCAD.Vector(topRight.x, -topRight.y + length/10 - fp.DiameterTweak, 0) | |
# baselineBottom2 = FreeCAD.Vector(0, -topRight.y + length/10 - fp.DiameterTweak, 0) | |
# | |
# ## Edges | |
# baseline2 = Part.makeLine(baselineBottom2, baselineTop2) | |
# leftSlant = Part.makeLine(baselineTop2, topArcPt2) | |
# topLine = Part.makeLine(topArcPt2, undercutBottom2) | |
# undercutLeft2 = Part.makeLine(undercutBottom2, undercutTop2) | |
# topRightLine2 = Part.makeLine(undercutTop2, topRight) | |
# rightLine = Part.makeLine(topRight, bottomRight) | |
# bottomLine2 = Part.makeLine(bottomRight, baselineBottom2) | |
# wire = Part.Wire([baseline2, leftSlant, topLine, undercutLeft2, topRightLine2, rightLine, bottomLine2]) | |
# face = Part.makeFace(wire,"Part::FaceMakerCheese") | |
# if not annular: | |
# face.Placement.move(FreeCAD.Vector(0,0,-offset)) | |
# return face | |
# | |
# def makeCantileverMate(self,fp): | |
# face = self.makeCantileverMateFace(fp) | |
# face.Placement.move(fp.Position) | |
# direction = FreeCAD.Vector(0,0,1) | |
# offset = fp.CantileverMateClearance | |
# extrude = face.extrude(direction*(fp.Width + 2*offset)) | |
# if fp.Symmetric or fp.Reversed: | |
# mirror = extrude.mirror(FreeCAD.Vector(0,0,0),FreeCAD.Vector(-1,0,0)) | |
# if fp.Symmetric: | |
# extrude = extrude.fuse(mirror) | |
# elif fp.Reversed: | |
# extrude = mirror | |
# extrude.Placement.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(-1,-1,-1),120) | |
# extrude.Placement.move(FreeCAD.Vector(0,-fp.Width/2,0)) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.Placement = FreeCAD.Placement() | |
# extrude.Placement.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),180) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.Placement = FreeCAD.Placement() | |
# extrude.rotate(extrude.Face1.CenterOfGravity,FreeCAD.Vector(0,0,1),fp.Angle) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.rotate(extrude.Face1.CenterOfGravity,FreeCAD.Vector(0,1,0),fp.AngleY) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.rotate(extrude.Face1.CenterOfGravity,FreeCAD.Vector(1,0,0),fp.AngleX) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# return extrude | |
# | |
# def makeMortiseTenon(self,fp): | |
# '''make the shape''' | |
# adjust = fp.Position | |
# topLeft = FreeCAD.Vector(-fp.Width/2,fp.Length/2,0).add(adjust) | |
# topRight = FreeCAD.Vector(fp.Width/2,fp.Length/2,0).add(adjust) | |
# bottomLeft = FreeCAD.Vector(-fp.Width/2,-fp.Length/2,0).add(adjust) | |
# bottomRight = FreeCAD.Vector(fp.Width/2,-fp.Length/2,0).add(adjust) | |
# midLeft = FreeCAD.Vector(-fp.Width/2,0,0).add(adjust) | |
# midRight = FreeCAD.Vector(fp.Width/2,0,0).add(adjust) | |
# top = Part.makeLine(topLeft,topRight) | |
# bottom = Part.makeLine(bottomLeft,bottomRight) | |
# if fp.Rounded: | |
# left = Part.makeCircle(fp.Length/2,midLeft,FreeCAD.Vector(0,0,1),90,270) | |
# right = Part.makeCircle(fp.Length/2,midRight,FreeCAD.Vector(0,0,1),270,90) | |
# else: | |
# left = Part.makeLine(topLeft, bottomLeft) | |
# right = Part.makeLine(topRight, bottomRight) | |
# wire = Part.Wire([top,right,bottom,left]) | |
# wire.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),fp.Angle) | |
# face = Part.makeFace(wire,"Part::FaceMakerCheese") | |
# if fp.Offset != 0: | |
# face = face.makeOffset2D(fp.Offset) | |
# if fp.Reversed: | |
# direction = FreeCAD.Vector(0,0,-1) | |
# else: | |
# direction = FreeCAD.Vector(0,0,1) | |
# if fp.JointType == "Mortise": | |
# direction = direction * (-1) | |
# extrude = face.extrude(direction*fp.Depth) | |
# if fp.Symmetric: | |
# extrude2 = face.extrude(direction*-1*fp.Depth) | |
# extrude = extrude.fuse(extrude2) | |
# extrude.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,1,0),fp.AngleY) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# extrude.rotate(FreeCAD.Vector(0,0,0),FreeCAD.Vector(1,0,0),fp.AngleX) | |
# extrude = extrude.transformShape(extrude.Placement.toMatrix(),True) | |
# return extrude | |
# | |
#class TaskEditLinkSubPanel: #simple editor for App::PropertyLinkSub | |
# def __init__(self, obj, linkSubName, subNames,): | |
# self.obj = obj | |
# self.subNames = subNames | |
# self.linkSubName = linkSubName #entire LinkSub property | |
# self.linkObj = getattr(self.obj,linkSubName)[0] | |
# self.subObjects = getattr(self.obj,linkSubName)[1] | |
# self.form = QtGui.QWidget() | |
# self.label1 = QtGui.QLabel("Select the "+self.subNames+" subobjects to use and click OK.\nThe ones already being utilized have been selected for you.") | |
# layout=QtGui.QHBoxLayout() | |
# layout.addWidget(self.label1) | |
# self.form.setLayout(layout) | |
# self.form.setWindowTitle('Edit '+self.subNames) | |
# self.obj.ViewObject.Visibility = False | |
# self.linkObj.ViewObject.Visibility = True | |
# FreeCADGui.Selection.clearSelection() | |
# for f in self.subObjects: | |
# FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.Name,self.linkObj.Name,f) | |
# self.obj.Proxy.editingMode = True | |
# | |
# def reject(self): | |
# FreeCADGui.Control.closeDialog() | |
# fp = self.obj | |
# self.linkObj.ViewObject.Visibility = False | |
# fp.ViewObject.Visibility = True | |
# self.obj.Proxy.editingMode = False #self.obj.Proxy is the Joint class object (self in that class) | |
# FreeCADGui.activeDocument().resetEdit() | |
# FreeCAD.ActiveDocument.recompute() | |
# | |
# def accept(self): | |
# FreeCADGui.ActiveDocument.resetEdit() | |
# FreeCADGui.Control.closeDialog() | |
# fp = self.obj | |
# if not fp: #user deleted or closed document perhaps | |
# return | |
# selx = FreeCADGui.Selection.getSelectionEx() | |
# if not selx: | |
# FreeCAD.Console.PrintWarning("Nothing selected, leaving "+self.linkObj.Name+" unmodified.") | |
# return | |
# seNames = [sen for sen in selx[0].SubElementNames] | |
# setattr(self.obj,self.linkSubName,(selx[0].Object,seNames)) #allow user to select diffent object | |
# self.linkObj = selx[0].Object | |
# if hasattr(fp,"_Body") and fp._Body and self.linkObj not in fp._Body.Group: | |
# fp._Body.Group += [self.linkObj] | |
# if not self.linkObj.isDerivedFrom("PartDesign::Feature"): | |
# fp.ClaimChildren = True | |
# self.linkObj.ViewObject.Visibility = False | |
# fp.ViewObject.Visibility = True | |
# if hasattr(fp.Proxy,"editingMode"): | |
# fp.Proxy.editingMode = False | |
# FreeCAD.ActiveDocument.recompute() | |
# | |
# | |
#class JointVP: | |
# | |
# def __init__(self, obj): | |
# '''Set this object to the proxy object of the actual view provider''' | |
# obj.Proxy = self | |
# | |
# def attach(self, obj): | |
# '''Setup the scene sub-graph of the view provider, this method is mandatory''' | |
# self.Object = obj.Object | |
# | |
# def updateData(self, fp, prop): | |
# '''If a property of the handled feature has changed we have the chance to handle this here''' | |
# # fp is the handled feature, prop is the name of the property that has changed | |
# pass | |
# | |
# def getDisplayModes(self,obj): | |
# '''Return a list of display modes.''' | |
# modes=[] | |
# modes.append("Flat Lines") | |
# return modes | |
# | |
# def setEdit(self,vobj,modNum): | |
# if modNum == 0: | |
# vobj.Object.Proxy.editFace() | |
# return True | |
# elif modNum == 3: | |
# FreeCADGui.runCommand('Part_ColorPerFace',0) | |
# return True | |
# | |
# def getDefaultDisplayMode(self): | |
# '''Return the name of the default display mode. It must be defined in getDisplayModes.''' | |
# return "Flat Lines" | |
# def setDisplayMode(self,mode): | |
# '''Map the display mode defined in attach with those defined in getDisplayModes.\ | |
# Since they have the same names nothing needs to be done. This method is optional''' | |
# return mode | |
# | |
# def onChanged(self, vp, prop): | |
# '''Here we can do something when a single property got changed''' | |
# #FreeCAD.Console.PrintMessage("Change property: " + str(prop) + ""+chr(10)) | |
# | |
# def claimChildren(self): | |
# if self.Object.ClaimChildren and self.Object.Face and not self.Object.Shape.isNull() and self.Object.Boolean: | |
# return [self.Object.Face[0]] | |
# else: | |
# return [] | |
# | |
# def onDelete(self, vobj, subelements): | |
# if vobj.Object.Face: | |
# vobj.Object.Face[0].ViewObject.Visibility = True | |
# if hasattr(vobj.Object,"_Body"): #do this only when the object is in a PD body | |
# #need to ensure the next feature in the tree's BaseFeature property points to our BaseFeature | |
# solids = [feat for feat in vobj.Object._Body.Group if feat.isDerivedFrom("PartDesign::Feature") and feat.BaseFeature == vobj.Object] | |
# if len(solids) == 1: #found previous solid feature | |
# solids[0].BaseFeature = vobj.Object.BaseFeature | |
# return True | |
# | |
# def getIcon(self): | |
# '''Return the icon in XPM format which will appear in the tree view. This method is\ | |
# optional and if not defined a default icon is shown.''' | |
# return ''' | |
#/* XPM */ | |
#static char *_637979355779[] = { | |
#/* columns rows colors chars-per-pixel */ | |
#"64 64 151 2 ", | |
#" c #1D1D0E0E3C3C", | |
#". c #56160CCD0CCD", | |
#"X c #5D370A6C0A6C", | |
#"o c #45450D0D1717", | |
#"O c #56D714951495", | |
#"+ c #575718181818", | |
#"@ c #62620D0D0D0D", | |
#"# c #6B150D0D0D0D", | |
#"$ c #74740D0D0D0D", | |
#"% c #79790D0D0D0D", | |
#"& c #7D270DB812BD", | |
#"* c #77770F0F1F1F", | |
#"= c #78780D0D1D1D", | |
#"- c #656511111313", | |
#"; c #717110101212", | |
#": c #5E5E0F0F2B2B", | |
#"> c #4B4B0F0F3BBC", | |
#", c #59DA0F0F3C3C", | |
#"< c #414111113333", | |
#"1 c #484810103333", | |
#"2 c #5D5D10103030", | |
#"3 c #66660E0E2424", | |
#"4 c #6BEC0F0F2D2D", | |
#"5 c #65650F0F3333", | |
#"6 c #6B6B0D0D3F3F", | |
#"7 c #73730D0D3636", | |
#"8 c #5D5D22222222", | |
#"9 c #63632D2D2D2D", | |
#"0 c #686835353535", | |
#"q c #0C8D0C8D562B", | |
#"w c #0AAE0AAE5CF5", | |
#"e c #1A1A10105151", | |
#"r c #1369136958AE", | |
#"t c #37370F0F4D4D", | |
#"y c #393911114949", | |
#"u c #33330F0F52D3", | |
#"i c #31310E0E5858", | |
#"p c #398F0DB85C5C", | |
#"a c #20A120A15D5D", | |
#"s c #0D0D0D0D64A4", | |
#"d c #0E0E0E0E6D6D", | |
#"f c #0D0D0D0D721C", | |
#"g c #0D0D0D0D7CBC", | |
#"h c #1C9D0E0E72F3", | |
#"j c #15400D637E53", | |
#"k c #111110107474", | |
#"l c #3A3A0D0D6F6F", | |
#"z c #29AA0E0E7777", | |
#"x c #37370D0D7373", | |
#"c c #2A2A2A2A6262", | |
#"v c #36B736B769EA", | |
#"b c #46460E0E4F4F", | |
#"n c #49090D8E4CCD", | |
#"m c #5E5E0D0D4C4C", | |
#"M c #49490D0D5F5F", | |
#"N c #76F74B4B4B4B", | |
#"B c #7F7F58585858", | |
#"V c #464646467373", | |
#"C c #535353537C7C", | |
#"Z c #84B70D740D74", | |
#"A c #8CE20D0D0D0D", | |
#"S c #947D0CB00CC7", | |
#"D c #9B9B0CA60CA6", | |
#"F c #81810D0D147B", | |
#"G c #90900E0E1515", | |
#"H c #9ADA0D0D1192", | |
#"J c #949412121212", | |
#"K c #A6FC0A430A60", | |
#"L c #ACEC0C4C0C4C", | |
#"P c #B3D80B300B30", | |
#"I c #BD0B0A590A59", | |
#"U c #A52513941394", | |
#"Y c #A1A11D9E1D9E", | |
#"T c #A3A32A2A2A2A", | |
#"R c #A4A435353535", | |
#"E c #A6A63DBE3DBE", | |
#"W c #C1C00A0A0A0A", | |
#"Q c #A8A845454545", | |
#"! c #AD2D58D958D9", | |
#"~ c #8A8A69696969", | |
#"^ c #921274F574F5", | |
#"/ c #AFAF63636363", | |
#"( c #B2B26E6E6E6E", | |
#") c #B4B477777777", | |
#"_ c #B6B67E7E7E7E", | |
#"` c #0D8E0D8E85C5", | |
#"' c #0D0D0CCD8D78", | |
#"] c #16970D8E8686", | |
#"[ c #13130F0F8888", | |
#"{ c #0CB70CA6941C", | |
#"} c #0C7A0C7A9C2E", | |
#"| c #12930D0D9A1A", | |
#" . c #139413949595", | |
#".. c #1C9D1C9D9313", | |
#"X. c #22220D0D8888", | |
#"o. c #252525259393", | |
#"O. c #2F2F2F2F9494", | |
#"+. c #3B3B3B3B9797", | |
#"@. c #0A0F0A0EA794", | |
#"#. c #0B200B20A900", | |
#"$. c #0B0B0B0BB333", | |
#"%. c #0A0A0A0ABEBE", | |
#"&. c #29292929A2A2", | |
#"*. c #4C4C4C4C9D1D", | |
#"=. c #62E362E38606", | |
#"-. c #747474749292", | |
#";. c #5C5C5C5CA323", | |
#":. c #6B6B6B6BA828", | |
#">. c #79797979AD2D", | |
#",. c #68686868B1B1", | |
#"<. c #0A0A0A0AC1B7", | |
#"1. c #810181019A1A", | |
#"2. c #A3A38E8E8E8E", | |
#"3. c #B9B989898989", | |
#"4. c #A8A897179717", | |
#"5. c #BDBD97979797", | |
#"6. c #92929292A5A5", | |
#"7. c #9F9F9F9FAEAE", | |
#"8. c #8A8A8A8AB333", | |
#"9. c #9A1A9A1AB939", | |
#"0. c #AEAEA0A0A0A0", | |
#"q. c #A1A1A1A1AFAF", | |
#"w. c #BFBFA0A0A0A0", | |
#"e. c #B5B5AAAAAAAA", | |
#"r. c #A6A6A6A6BEBE", | |
#"t. c #BBBBB2B2B2B2", | |
#"y. c #BFBFB8B8B8B8", | |
#"u. c #C2C2A7A7A7A7", | |
#"i. c #C5C5B0B0B0B0", | |
#"p. c #CACABEBEBEBE", | |
#"a. c #ACACACACC2C2", | |
#"s. c #B6B6B6B6C7C7", | |
#"d. c #BFBFBFBFC6C6", | |
#"f. c #C81DC51AC8C8", | |
#"g. c #CFCFC8C8C8C8", | |
#"h. c #D3D3CECECECE", | |
#"j. c #C6C6C6C6D0D0", | |
#"k. c #CDCDCDCDD151", | |
#"l. c #D4D4D3D3D3D3", | |
#"z. c #D9D9D7D7D7D7", | |
#"x. c #D4D4D4D4DA5A", | |
#"c. c #DADAD9D9DA84", | |
#"v. c #E0E0DFDFDFDF", | |
#"b. c #DFDFDFDFE2E2", | |
#"n. c #E3E3E37CE416", | |
#"m. c #E8E8E7E7E7E7", | |
#"M. c #E7E7E7E7E8E8", | |
#"N. c #ECACECACED2C", | |
#"B. c #F4BDF4BDF62C", | |
#"V. c #F426F426FB94", | |
#"C. c None", | |
#/* pixels */ | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.V.B.V.B.B.V.V.V.V.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.7.v w w w w w g @.@.@.@.@.", | |
#"f.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.m.1.a w w w q s ' @.@.@.@.@.@.@.", | |
#"X + B 4.z.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.k.=.r w w s q f { @.@.@.@.@.@.@.@.@.", | |
#"X X X X X 9 ^ t.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.r.V w w w w q g @.@.@.@.@.@.@.@.@.@.@.@.", | |
#"L Z # . X X X X O N 2.l.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.N.6.c w w w w s ` @.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W I S % @ . X X X X 8 ~ e.m.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.c.-.q w w w q f ' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W L A # . X X X X . N ^ g.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.d.C q w s w q f } @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W I K % @ . @ @ X X + B 0.v.C.C.C.C.C.C.C.C.C.C.C.B.q.v w w w w w g @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W I A $ . X X X X . 0 ^ y.B.C.C.C.C.C.M.1.a w w w q s ' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W I W W W U Z # . X X X X + N 2.c.k.=.r s w w q d } @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W I S $ X . X X o q w w w q g @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W L Z e q q w q s ` @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W P #.%.} d { @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W P #.<.<.{ @.@.} @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W } <.<.} #.h 1 @.@.@.@.@.@.@.@.@.@.@.@.@.@.$.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W I [ #.$.' t S 4 @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W D D S ; K K 4 @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W D K K = @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W S L K & @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.$.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W D K K F @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W I D K K F @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W I L W W D K K F @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W z b K D K K F @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W X.<.#.y > t h @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W z <.<.| @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W l <.<.@.} @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W x <.<.} @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W x <.<.} @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W x <.<.@.} @.@.@.@.@.@.@.@.@.@.@.$.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W l <.<.@.@.@.u { @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W M <.<.$.j : D ' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W G * 5 2 % K K ' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W P S K D ' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W P S K K j @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W P S K K j @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.$.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W I A K K ] @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W I W W W A K K j @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 5 , L I A K K j @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W m <.| , - 3 > ' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W m <.<.%.` @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 6 <.<.%.' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 7 <.<.<.' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 7 <.<.<. .@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 7 <.<.<.' @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 7 <.<.<.' @.{ j @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.#.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W 7 <.<.<.' h F p @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W H n p p < D K p @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.| ;.j.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W A K K p @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@...>.n.C.C.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W A K K p @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.O.9.B.C.C.C.C.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W A K K b @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.} *.s.B.C.C.C.C.C.C.", | |
#"W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W A K K b @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@. .:.x.C.C.C.C.C.C.C.C.C.", | |
#"_ E L W W W I W W W W W W W W W W W W W W W W W W W W W W W W A K K n @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.o.8.m.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.n.u./ Y I W W W W W W W W W W W W W W W W W W W W U 4 I W S K K n @.@.@.@.@.@.@.@.@.@.@.@.} +.a.V.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.h.3.Q U W W W W W W W W W W W W W W W W W H <.] 4 A S & i @.@.@.@.@.@.@.@.@.@.} ;.f.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.B.i.( T P W W W W W W W W W W W W W H <.<.<.k ' @.@.@.@.@.@.@.@.@.@...>.b.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.z.5.! U I W W W W W W W W W H <.<.<.{ @.@.@.@.@.@.@.@.@.O.9.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.p.) R P W W W W W W K $.<.<.{ @.@.@.@.@.@.} *.s.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.n.w.! J I W W L $.<.<.{ @.@.@.@. .:.x.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.g.3.Q J #.<.<.{ @.@.o.8.m.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.N.a.,.&.` +.a.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.v.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.", | |
#"C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C." | |
#}; | |
#''' | |
# def __getstate__(self): | |
# '''When saving the document this object gets stored using Python's json module.\ | |
# Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ | |
# to return a tuple of all serializable objects or None.''' | |
# return None | |
# def __setstate__(self,state): | |
# '''When restoring the serialized object from document we have the chance to set some internals here.\ | |
# Since no data were serialized nothing needs to be done here.''' | |
# return None | |
############ | |
#if __name__ == "__main__": | |
# FreeCAD.Console.PrintMessage("Please run Joint.FCMacro rather than this file.\n") | |
############ | |
#CODE_ENDS_HERE | |
BASENAME = 'joint' | |
def getBody(feature): | |
doc = FreeCAD.ActiveDocument | |
bodies = [obj for obj in doc.Objects if obj.TypeId == "PartDesign::Body"] | |
for bod in bodies: | |
if feature in bod.Group: | |
return bod | |
return None | |
def getJointType(): | |
'''ask user the joint type to make, return as string''' | |
from PySide import QtCore,QtGui | |
window = QtGui.QApplication.activeWindow() | |
items = ["Mortise","Tenon","Box Joint","Dovetail Joint","Cantilever Hook", "Cantilever Mate","Annular Hook","Annular Mate","Ball Joint","Ball Mate","Split Joint", "Split Mate","Cancel"] | |
caption = "Select joint type" | |
item,ok = QtGui.QInputDialog.getItem(window,"Joint type",caption,items,False) | |
if ok: | |
return item | |
else: | |
return None | |
def makeObject(FP): | |
import DraftGeomUtils as DGU | |
doc = FreeCAD.ActiveDocument | |
body=None | |
selobjs = FreeCADGui.Selection.getSelectionEx() | |
if doc: | |
doc.openTransaction("Create Joint") | |
if selobjs: | |
for selobj in selobjs: | |
body = getBody(selobj.Object) | |
jType = getJointType() | |
if not jType: | |
continue | |
if jType == "Tenon" or jType == "Cantilever Hook" or jType == "Annular Hook" or jType == "Ball Joint" or jType == "Split Joint": | |
orientation = "External" | |
else: | |
orientation = "Internal" | |
if body: | |
if orientation == "External": | |
joint = body.newObject("PartDesign::FeatureAdditivePython", "Joint") | |
else: | |
joint = body.newObject("PartDesign::FeatureSubtractivePython", "Joint") | |
else: | |
joint = doc.addObject("Part::FeaturePython","Joint") | |
FP.Joint(joint) | |
FP.JointVP(joint.ViewObject) | |
# joint.AddSubType = "Additive" if bool(jType == "Tenon" or jType == "Cantilever Hook" or jType == "Annular Hook" or jType == "Ball Joint" or jType == "Split Joint") else "Subtractive" | |
joint.Width = joint.Width * 10 if "Box" in jType or "Dovetail" in jType else joint.Width | |
joint.Depth = 10 if "Split" in jType else joint.Depth | |
joint.Proxy.execute(joint) | |
if body: | |
joint.ClaimChildren = False #do not claim children in Part Design to preserve the linear tree | |
if selobj.HasSubObjects: | |
faces = [name for name in selobj.SubElementNames if "Face" in name] | |
if faces and len(faces) == 1 and DGU.isPlanar(selobj.Object.getSubObject(faces[0])): | |
joint.Face = (selobj.Object,faces) | |
else: | |
FreeCAD.Console.PrintError("Select 1 (planar) face of an object. Using Face1\n") | |
joint.Face = (selobj.Object, ["Face1"]) | |
else: | |
joint.Face = (selobj.Object, ["Face1"]) | |
FreeCAD.Console.PrintError("Select 1 (planar) face of an object.\n") | |
joint.JointType = jType | |
if "Cantilever" in jType or "Annular" in jType: | |
joint.Depth = 15 | |
joint.Length = 3 | |
else: | |
FreeCAD.Console.PrintMessage("No face selected to make a joint on.\n") | |
doc.commitTransaction() | |
doc.recompute() | |
def writeFile(): | |
with open(py_file,"w") as outfile: | |
for line in code.splitlines(): | |
if "#CODE_ENDS_HERE" in line: | |
break | |
if line.startswith('#'): | |
if line == "# -*- coding: utf-8 -*-": | |
line = "#" + line | |
outfile.write(line[1:]+"\n") #skip first character (#) | |
if __name__ == "__main__": | |
import os | |
fin = open(__file__, 'r') | |
code = fin.read() | |
fin.close() | |
version = code.splitlines()[1][16:] | |
real_path = os.path.realpath(__file__) | |
dir_path = os.path.dirname(real_path) | |
py_file = real_path.replace(".FCMacro",".py").replace('Joint','joint') | |
bHasFile = os.path.exists(py_file) | |
noImport = False #user elects not to save import file | |
if not bHasFile: | |
from PySide import QtCore,QtGui | |
window = QtGui.QApplication.activeWindow() | |
items = ["Yes, go ahead and create the file.", "No, do not create the file.","Cancel"] | |
caption = "In order for "+BASENAME+" objects to be parametric after saving and reloading file\n\ | |
we need to create another file on this computer. File to be created will be: \n\n"+py_file+"\n\n\ | |
This makes it available to the system upon restarting FreeCAD and loading documents containing the \n\ | |
"+BASENAME+" feature python objects. May we proceed?\n\n" | |
item,ok = QtGui.QInputDialog.getItem(window,"One time installation",caption,items) | |
if ok and item == items[0]: | |
writeFile() | |
QtGui.QMessageBox.information(window,"Success","File successfully created. Please note: if you uninstall "+BASENAME+" macro you need to manually remove this file, too.\n") | |
else: | |
new_lines = [] | |
for line in code.splitlines(): | |
if line.startswith('#'): | |
if "CODE_ENDS_HERE" in line: | |
break | |
if line == "# -*- coding: utf-8 -*-": | |
new_lines.append(line+"\n") | |
continue | |
new_lines.append(line[1:]+"\n") | |
code = "".join(new_lines) | |
#credit to Mila Nautikus for his answer to a question on stackoverflow, which I modified here | |
#in this example the filename is bevel.py | |
#https://stackoverflow.com/questions/5362771/how-to-load-a-module-from-code-in-a-string | |
########## | |
import sys, importlib | |
my_name = 'joint' #filename = joint.py, so this must be 'joint' | |
my_spec = importlib.util.spec_from_loader(my_name, loader=None) | |
joint = importlib.util.module_from_spec(my_spec) | |
exec(code, joint.__dict__) | |
sys.modules['joint'] = joint | |
makeObject(joint) | |
noImport = True | |
if not noImport: #don't never use no double negatives | |
import joint as FP | |
import addonmanager_utilities as utils | |
if FP.__version__ != __version__: | |
writeFile() | |
from PySide import QtCore,QtGui | |
window = QtGui.QApplication.activeWindow() | |
mbox = QtGui.QMessageBox() | |
mbox.setWindowTitle(BASENAME+" updated") | |
mbox.setText(BASENAME+".py has been updated to version "+__version__+". \ | |
You must restart FreeCAD for the new changes to take effect and to use the macro.") | |
mbox.setIcon(mbox.Warning) | |
mbox.setStandardButtons(mbox.Ok | mbox.Cancel) | |
mbox.setDefaultButton(mbox.Cancel) | |
okBtn = mbox.button(QtGui.QMessageBox.StandardButton.Ok) | |
cancelBtn = mbox.button(QtGui.QMessageBox.StandardButton.Cancel) | |
okBtn.setText("Restart now") | |
cancelBtn.setText("Restart later") | |
ret = mbox.exec_() | |
if ret == mbox.Ok: | |
QtCore.QTimer.singleShot(1000, utils.restart_freecad) | |
else: | |
makeObject(FP) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment