# Involute Gears Generation Script # by Marcin Wanczyk (dj_who) # (c) 2011 LGPL import FreeCAD import FreeCADGui import Part import Draft import MeshPart import Mesh import math from PySide import QtGui, QtCore App = FreeCAD Gui = FreeCADGui def proceed(): try: compute() except Exception: hide() QtGui.QApplication.restoreOverrideCursor() def compute(): QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) if FreeCAD.ActiveDocument is None: FreeCAD.newDocument("Gear") oldDocumentObjects = App.ActiveDocument.Objects try: N = int(l1.text()) p = float(l2.text()) alfa = int(l3.text()) y = float(l4.text()) # standard value y<1 for gear drives y>1 for Gear pumps m = p/math.pi # standard value 0.06, 0.12, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 60 (polish norm) c = float(l5.text())*m # standard value 0,1*m - 0,3*m j = float(l6.text())*m # standard value 0,015 - 0,04*m width = float(l7.text()) # gear width except ValueError: FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n") # tooth height h = 2*y*m+c # pitch diameter d = N*m # root diameter df = d - 2*y*m - 2*c # df=d-2hf where and hf=y*m+c # addendum diameter da = d + 2*y*m # da=d+2ha where ha=y*m # base diameter for involute db = d * math.cos(math.radians(alfa)) #Base circle baseCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "BaseCircle") Draft._Circle(baseCircle) Draft._ViewProviderDraft(baseCircle.ViewObject) baseCircle.Radius = db/2 baseCircle.FirstAngle = 0.0 baseCircle.LastAngle = 0.0 # Root circle rootCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "RootCircle") Draft._Circle(rootCircle) Draft._ViewProviderDraft(rootCircle.ViewObject) rootCircle.Radius = df/2 rootCircle.FirstAngle = 0.0 rootCircle.LastAngle = 0.0 # Addendum circle addendumCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "AddendumCircle") Draft._Circle(addendumCircle) Draft._ViewProviderDraft(addendumCircle.ViewObject) addendumCircle.Radius = da/2 addendumCircle.FirstAngle = 0.0 addendumCircle.LastAngle = 0.0 # Pitch circle pitchCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PitchCircle") Draft._Circle(pitchCircle) Draft._ViewProviderDraft(pitchCircle.ViewObject) pitchCircle.Radius = d/2 pitchCircle.FirstAngle = 0.0 pitchCircle.LastAngle = 0.0 #************ Calculating right sides of teeth # Involute of base circle involute = [] involutee = [] involutesav = [] for t in range(0, 60, 1): x = db/2*(math.cos(math.radians(t))+math.radians(t)*math.sin(math.radians(t))) y = db/2*(math.sin(math.radians(t))-math.radians(t)*math.cos(math.radians(t))) involute.append(Part.Vertex(x, y, 0).Point) #************ Drawing right sides of teeth involutesav.extend(involute) involutee.extend(involute) for angle in range(1, N+1, 1): involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "InvoluteL" + str(angle)) involutee.insert(0, (0, 0, 0)) involuteshape = Part.makePolygon(involutee) involuteobj.Shape=involuteshape involutee = [] for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point involutee.insert(0,pointt) involute.extend(involutesav) involutee = [] #************ Calculating difference between tooth spacing on BaseCircle and PitchCircle pc = App.ActiveDocument.getObject("PitchCircle") inv = App.ActiveDocument.getObject("InvoluteL1") cut = inv.Shape.cut(pc.Shape) # FreeCAD.ActiveDocument.addObject("Part::Feature","CutInv").Shape=cut invPoint = cut.Vertexes[0].Point diff = invPoint.y*2 # instead of making axial symmetry and calculating point distance. anglediff = 2*math.asin(diff/d) #************ Calculating left sides of teeth #************ Inversing Involute for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex(point.x, point.y*-1, 0).Point involutee.insert(0, pointt) involute.extend(involutee) involutee = [] #Normal tooth size calculated as: 0,5* p - j j = m * 0,1 below are calculations # 0,5* p - m * 0,1 # 0,5* p - p /pi * 0,1 # 0,5*360/N - ((360/N)/pi)* 0,1 # 0,5*360/N - (360/N)*((1/pi)*0,1) j = (p/pi)*0,1 # 0,5*360/N - (360/N)*((p/pi)*0,1)/p # 0,5*360/N - (360/N)*( j )/p for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex(point.x*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff) - point.y*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff),point.x*math.sin(math.radians(180/N-(360/N)*(j/p))+anglediff) + point.y*math.cos(math.radians(180/N-(360/N)*(j/p))+anglediff),0).Point involutee.insert(0, pointt) involute.extend(involutee) involutesav = [] involutesav.extend(involute) #************ Drawing left sides of teeth for angle in range(1, N+1, 1): involuteobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "InvoluteR" + str(angle)) involutee.insert(0, (0, 0, 0)) involuteshape = Part.makePolygon(involutee) involuteobj.Shape = involuteshape involutee = [] for num in range(0,60,1): point = involute.pop() pointt = Part.Vertex(point.x*math.cos(math.radians(angle*360/N)) - point.y*math.sin(math.radians(angle*360/N)),point.x*math.sin(math.radians(angle*360/N)) + point.y*math.cos(math.radians(angle*360/N)),0).Point involutee.insert(0,pointt) involute.extend(involutesav) Gui.SendMsgToActiveView("ViewFit") #************ Forming teeth cutCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutCircle") Draft._Circle(cutCircle) Draft._ViewProviderDraft(cutCircle.ViewObject) cutCircle.Radius = da # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be. cutCircle.FirstAngle = 0.0 cutCircle.LastAngle = 0.0 cutTool = cutCircle.Shape.cut(addendumCircle.Shape) # cutshape = Part.show(cutTool) gearShape = rootCircle.Shape for invNum in range(1, N+1, 1): invL = App.ActiveDocument.getObject("InvoluteL" + str(invNum)) invR = App.ActiveDocument.getObject("InvoluteR" + str(invNum)) cutL = invL.Shape.cut(cutTool) cutR = invR.Shape.cut(cutTool) pointL = cutL.Vertexes.pop().Point pointR = cutR.Vertexes.pop().Point faceEdge = Part.makeLine(pointL, pointR) toothWhole = cutL.fuse(cutR) toothWhole = toothWhole.fuse(faceEdge) toothWire = Part.Wire(toothWhole.Edges) toothShape = Part.Face(toothWire) # tooth = App.ActiveDocument.addObject("Part::Feature", "Tooth" +str(invNum)) # tooth.Shape=toothShape gearShape = gearShape.fuse(toothShape) for o in App.ActiveDocument.Objects: if oldDocumentObjects.count(o) == 0: App.ActiveDocument.removeObject(o.Name) gearFlat = App.ActiveDocument.addObject("Part::Feature", "GearFlat") gearFlat.Shape = gearShape Gui.ActiveDocument.getObject(gearFlat.Name).Visibility = False gear = App.ActiveDocument.addObject("Part::Extrusion", "Gear3D") gear.Base = gearFlat gear.Dir = (0, 0, width) App.ActiveDocument.recompute() if c1.isChecked(): gearMesh = App.ActiveDocument.addObject("Mesh::Feature", "Gear3D-mesh") faces = [] triangles = gear.Shape.tessellate(1) # the number represents the precision of the tessellation) for tri in triangles[1]: face = [] for i in range(3): vindex = tri[i] face.append(triangles[0][vindex]) faces.append(face) mesh = Mesh.Mesh(faces) gearMesh.Mesh = mesh App.ActiveDocument.removeObject(gear.Name) App.ActiveDocument.removeObject(gearFlat.Name) App.ActiveDocument.recompute() Gui.SendMsgToActiveView("ViewFit") QtGui.QApplication.restoreOverrideCursor() hide() def hide(): dialog.hide() dialog = QtGui.QDialog() dialog.resize(200,450) dialog.setWindowTitle("Gear") la = QtGui.QVBoxLayout(dialog) t1 = QtGui.QLabel("Number of teeth (N)") la.addWidget(t1) l1 = QtGui.QLineEdit() l1.setText("16") la.addWidget(l1) t2 = QtGui.QLabel("Circular pitch (p)") la.addWidget(t2) l2 = QtGui.QLineEdit() l2.setText("1.65") la.addWidget(l2) t3 = QtGui.QLabel("Pressure angle (alfa)") la.addWidget(t3) l3 = QtGui.QLineEdit() l3.setText("20") la.addWidget(l3) t4 = QtGui.QLabel("Tooth height factor (y)") la.addWidget(t4) l4 = QtGui.QLineEdit() l4.setText("1.0") la.addWidget(l4) t5 = QtGui.QLabel("Tooth clearance (c)") la.addWidget(t5) l5 = QtGui.QLineEdit() l5.setText("0.1") la.addWidget(l5) t6 = QtGui.QLabel("Tooth lateral clearance (j)") la.addWidget(t6) l6 = QtGui.QLineEdit() l6.setText("0.04") la.addWidget(l6) t7 = QtGui.QLabel("Gear width") la.addWidget(t7) l7 = QtGui.QLineEdit() l7.setText("6.0") la.addWidget(l7) c1 = QtGui.QCheckBox("Create as a Mesh") la.addWidget(c1) e1 = QtGui.QLabel("(for faster rendering)") commentFont = QtGui.QFont("Times", 8, True) e1.setFont(commentFont) la.addWidget(e1) okbox = QtGui.QDialogButtonBox(dialog) okbox.setOrientation(QtCore.Qt.Horizontal) okbox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) la.addWidget(okbox) QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), proceed) QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), hide) QtCore.QMetaObject.connectSlotsByName(dialog) dialog.show()