# 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()