A modified gear.py with bevel, helical angle

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
kevinchentw
Posts: 4
Joined: Sun Sep 01, 2013 7:56 am

A modified gear.py with bevel, helical angle

Post by kevinchentw »

Hi,
I hv modified origin gear.py in FreeCAD 0.14 to fit my work.
It can handle bevel and helical gears, also combined ones.
Comments are welcomed.
Hope it would be helpful.

Code: Select all

#Involute Gears Generation Script
#by Marcin Wanczyk (dj_who)
#(c) 2011 LGPL

# modified by Kevin Chen ( kevinttl@gmail.com ) Sep 1, 2013
# delevoped on Ubuntu

from __future__ import division # allows floating point division from integers
import FreeCAD, FreeCADGui, Part, Draft, math	# , MeshPart, Mesh
from FreeCAD import Base
from PyQt4 import QtGui,QtCore
App = FreeCAD
Gui = FreeCADGui

class InvoluteGear:
    def __init__(self, obj,prop_n,prop_m,prop_alfa,prop_a,prop_b,prop_width,prop_c,prop_j,prop_bevel,prop_helical):
        ''' Add the properties:  Number of Gears, Pressure Angle, Tooth Height Factor, Module, Tooth Clearance, Tooth Lateral Clearance, Width, Bevel Angle'''
        obj.addProperty("App::PropertyInteger","N","InvoluteGear","Number of Gears").N=prop_n
        obj.addProperty("App::PropertyFloat","m","InvoluteGear","Module").m=prop_m
        obj.addProperty("App::PropertyAngle","alfa","InvoluteGear","Pressure Angle").alfa=prop_alfa
        obj.addProperty("App::PropertyFloat","a","InvoluteGear","Addendum Factor (* Module)").a=prop_a
        obj.addProperty("App::PropertyFloat","b","InvoluteGear","Addendum Factor (* Module)").b=prop_b
        obj.addProperty("App::PropertyLength","width","InvoluteGear","Gear Width").width=prop_width
        obj.addProperty("App::PropertyLength","c","InvoluteGear","Tooth Clearance").c=prop_c
        obj.addProperty("App::PropertyLength","j","InvoluteGear","Tooth Lateral Clearance").j=prop_j
        obj.addProperty("App::PropertyAngle","bevel","InvoluteGear","Bevel Angle").bevel=prop_bevel
        obj.addProperty("App::PropertyAngle","helical","InvoluteGear","Helical Angle").helical=prop_helical
        obj.Proxy = self

    def onChanged(self, fp, prop):
        if prop == "N" or prop == "m" or prop == "alfa" or prop == "a" or prop == "b" or prop == "width" or prop == "c" or prop == "j" or prop == "bevel" or prop == "helical" : #if one of these is changed
            self.execute(fp)

    def execute(self, fp): #main part of script
        N = fp.N          
        alfa = fp.alfa
        m=fp.m      # module -> pitch diameter
        a = fp.a * m
        b = fp.b * m
        p = m * math.pi         # circle pitch
        c = fp.c* m     #standard value 0,1*m - 0,3*m
        j = fp.j * m     #standard value 0,015 - 0,04*m
        width = fp.width   #gear width
        bevel = fp.bevel
        helical = fp.helical
        if bevel > 70 or bevel < - 70 :
            FreeCAD.Console.PrintError("Bevel Angle should be in -70 ~ 70 degrees\n")
            return            
        # some consts
        dv_angle = math.radians(360 / N)
        
        #pitch diameter
        d=N*m

        #root diameter    
        df = d - 2 * b * m - 2 * c              #df=d-2hf where and hf=y*m+c 

        #addendum diameter    
        da = d + 2 * a * m                    #da=d+2ha where ha=y*m
    
        #base diameter for involute
        db=d * math.cos(math.radians(alfa))
        
        if df > db :
            # root is larger than base... not allowed!
            FreeCAD.Console.PrintError("Root diameter:" + str(df) + " is larger than Base diameter:" + str(db) + "\n")
            return
            
#        FreeCAD.Console.PrintError("pitch diameter=" + str(d) + ",base=" + str(db) + ",root=" + str(df) + "\n")
        rb = db / 2 # base radius

        #Root circle
        rootCircleRadius = df / 2

        # solve addendum angle dirctly
        # r = rb * sqrt(1 + t * t)
        # so at addendum situation, r = da / 2
        # (d + 2 * y * m) / 2 = (d * cos(alfa) / 2) * sqrt(1 + t * t)
        # sqrt(1 + t * t) = (1 + 2 * y * m / d) / cos(alfa)
        # t * t = ((1 + 2 * y * m / d) / cos(alfa)) ^ 2 - 1
        # t = sqrt(((1 + 2 * y * m / d) / cos(alfa)) ^ 2 - 1)
        # pitch is a very important point! so....
        pt_angle_invaa = math.cos(math.radians(alfa))
        pt_angle = math.sqrt(1 / (pt_angle_invaa * pt_angle_invaa) - 1)
        # stop angle
        st_angle_aa = (1 + 2 * a * m / d) / pt_angle_invaa
        st_angle = math.sqrt(st_angle_aa * st_angle_aa - 1)
        # check angle (make sure addendum is reached!
        ck_angle = (st_angle - math.atan(st_angle)) * 2
        # angle steps -> around 5 pts / mm
        # divide into above pitch and below evenly as possible...
        n_ang_ap = int((da - d) * 2.5)
        if n_ang_ap < 2 :
            n_ang_ap = 2
        n_ang_bp = int((d - db) * 2.5)
        if n_ang_bp < 2 :
            n_ang_bp = 2
        n_angle = n_ang_ap + n_ang_bp - 1   # pitch point is dup

        #************ Calculating right sides of teeth
        #Involute of base circle
        involute=[]
        involutee=[]
        involutesav=[]

#        FreeCAD.Console.PrintError("n_angle=" + str(n_angle) + ",st_angle=" + str(st_angle) + ",ck_angle=" + str(ck_angle) + "\n")
        for t in range(0,n_angle,1):
            if t < n_ang_bp :
                rang = pt_angle * t / (n_ang_bp - 1)
            else :
                rang = pt_angle + (st_angle - pt_angle) * (t - n_ang_bp + 1) / (n_ang_ap - 1)
            x = rb * (math.cos(rang) + rang * math.sin(rang))
            y = rb * (math.sin(rang) - rang * math.cos(rang))
            if t == 0:
                rAngle = math.atan2(y,x)
            involute.append(Part.Vertex(x,y,0).Point)
#        FreeCAD.Console.PrintError("cal finish\n")

        #************ Drawing rigth sides of teeth
        involutesav.extend(involute)
        involutee.extend(involute)

        startAngle = []
        stopAngle = []
        
        involuteL = [ 0 ]       # index 0 is useless
#        FreeCAD.Console.PrintError("#0.1\n")
        sAngle = dv_angle
        for angle in range(1,N+1,1):
            involutee.insert(0,(rootCircleRadius * math.cos(rAngle),rootCircleRadius * math.sin(rAngle),0))        
            involuteshape = Part.makePolygon(involutee)
            involuteL.insert(angle,involuteshape)
            involutee=[]
            stopAngle.append(sAngle)
            for num in range(0,n_angle,1):
                point=involute.pop()
                x = point.x*math.cos(sAngle) - point.y*math.sin(sAngle)
                y = point.x*math.sin(sAngle) + point.y*math.cos(sAngle)
                pointt=Part.Vertex(x,y,0).Point
                involutee.insert(0,pointt)
            rAngle = math.atan2(y,x)
            involute.extend(involutesav)
            sAngle += dv_angle
        involutee=[]
    
        #************ Calculating difference between tooth spacing on BaseCircle and PitchCircle

#        cut = involuteL[1].cut(pitchCircle)
        #    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.
        diff = involuteL[1].Vertexes[n_ang_bp].Point.y * 2
        anglediff = math.radians(180 / N) - dv_angle * (j / p)
        if anglediff < ck_angle :
            # addendum is not reachable
            FreeCAD.Console.PrintError("Tooth angle:" + str(anglediff) + " is less than Check angle:" + str(ck_angle) + "\nSo addendum (a) is not reachable\n")
            return
        anglediff += 2 * math.asin(diff / d)

        #************ Calculating left sides of teeth

#        FreeCAD.Console.PrintError("#0.2\n")
        #************ Inversing Involute
        for num in range(0,n_angle,1):
            point=involute.pop()
            pointt=Part.Vertex(point.x,- point.y,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
#        FreeCAD.Console.PrintError("#0.3\n")
        for num in range(0,n_angle,1):
            point=involute.pop()
            x = point.x*math.cos(anglediff) - point.y*math.sin(anglediff)
            y = point.x*math.sin(anglediff) + point.y*math.cos(anglediff)
            pointt=Part.Vertex(x,y,0).Point
            involutee.insert(0,pointt)
        rAngle = math.atan2(y,x)
        involute.extend(involutee)
        involutesav=[]
        involutesav.extend(involute)

        #************ Drawing left sides of teeth
        involuteR = [0]     # index 0 is useless
#        FreeCAD.Console.PrintError("#0.4\n")
        sAngle = dv_angle
        for angle in range(1,N+1,1):
            involutee.insert(0,(rootCircleRadius * math.cos(rAngle),rootCircleRadius * math.sin(rAngle),0))        
            involuteshape = Part.makePolygon(involutee)
            involuteR.insert(angle,involuteshape)
            involutee=[]
            startAngle.append(rAngle)
            for num in range(0,n_angle,1):
                point=involute.pop()
                x = point.x*math.cos(sAngle) - point.y*math.sin(sAngle)
                y = point.x*math.sin(sAngle) + point.y*math.cos(sAngle)
                pointt=Part.Vertex(x,y,0).Point
                involutee.insert(0,pointt)
            rAngle = math.atan2(y,x)
            involute.extend(involutesav)
            sAngle += dv_angle

        #************ Forming teeth

#        cutCircleE = Part.makeCircle(da)    # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be.
#        cutCircleW = Part.Wire(cutCircleE)
#        cutCircle = Part.Face(cutCircleW)

#        cutTool=cutCircle.cut(addendumCircle)
        #    cutshape=Part.show(cutTool)
#        FreeCAD.Console.PrintError("#1\n")

        for invNum in range(1,N+1,1):
#            FreeCAD.Console.PrintError("invNum=" + str(invNum) + "\n")
            invL = involuteL[invNum]
            invR = involuteR[invNum]
            pointL = invL.Vertexes.pop().Point
            pointR = invR.Vertexes.pop().Point
            faceEdge=Part.makeLine(pointL,pointR)

            toothWhole = invL.fuse(invR)
            toothWhole=toothWhole.fuse(faceEdge)
            toothWire=Part.Wire(toothWhole.Edges)
#            toothWire = Part.Wire([invL,invR,faceEdge])

            if invNum == 1:
                gearedge = toothWire
            else:
                gearedge = Part.Wire([gearedge,toothWire])

            toothArc = Part.makeCircle(rootCircleRadius,FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),math.degrees(startAngle[invNum - 1]),math.degrees(stopAngle[invNum - 1]))
#            FreeCAD.Console.PrintError("#1.3 " + str(rootCircleRadius) + "," +  str(math.degrees(startAngle[invNum - 1])) + "," + str(math.degrees(stopAngle[invNum - 1])) + "\n")
            gearedge = Part.Wire([gearedge,toothArc])
#            FreeCAD.Console.PrintError("#1.4\n")

#        FreeCAD.Console.PrintError("#2\n")
        gearShape=Part.Face(gearedge)
        if bevel == 0 and helical == 0:
            ext = gearShape.extrude(Base.Vector(0,0,width))
        else :
            gearTop = gearShape
            tr = Base.Matrix()
            tr.move(Base.Vector(0,0,width))
            if helical != 0 :
                tr.rotateZ(math.radians(helical))
            if bevel != 0 :
                ss = 1 + width * math.tan(bevel) / d
                tr.scale(ss,ss,1)
            gearTop = gearTop.transformGeometry(tr)
            extloft = Part.makeLoft([gearedge,Part.Wire(gearTop.Edges)])
#            extsh1 = Part.Shell([gearShape,gearTop])
#            extsh2 = Part.Shell([extsh1,extloft])
            extsh1 = extloft.fuse(Part.Face(gearedge))
            extsh2 = extsh1.fuse(gearTop)
            ext = Part.Solid(extsh2)
        fp.Shape = ext      # result

default_n = 16
default_m = 1.0
default_alfa = 20
default_a = 1.1
default_b = 1.4
default_c = 0.1
default_j = 0.04
default_width = 6.0
default_bevel = 0.0
default_helical = 0.0

def makeInvoluteGear():
    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument()
    igearblock=doc.addObject("Part::FeaturePython","InvoluteGear") #add object to document
    igearblock.Label = "Involute Gear"
    InvoluteGear(igearblock,default_n,default_m,default_alfa,default_a,default_b,default_width,default_c,default_j,default_bevel,default_helical)
    igearblock.ViewObject.Proxy=0
    App.ActiveDocument.recompute()
    Gui.SendMsgToActiveView("ViewFit")


l1 = QtGui.QLineEdit()
l1.setText(str(default_n))
l2 = QtGui.QLineEdit()
l2.setText(str(default_m))
l3 = QtGui.QLineEdit()
l3.setText(str(default_alfa))
l4 = QtGui.QLineEdit()
l4.setText(str(default_a))
l5 = QtGui.QLineEdit()
l5.setText(str(default_c))
l6 = QtGui.QLineEdit()
l6.setText(str(default_j))
l7 = QtGui.QLineEdit()
l7.setText(str(default_width))
l8 = QtGui.QLineEdit()
l8.setText(str(default_bevel))
l9 = QtGui.QLineEdit()
l9.setText(str(default_b))
l10 = QtGui.QLineEdit()
l10.setText(str(default_helical))

def proceed():
    try:
        compute()
    except:
        hide()
        QtGui.qApp.restoreOverrideCursor()

def compute():    
    QtGui.qApp.restoreOverrideCursor()
    hide()
    try:
        N = int(l1.text())   
        m = float(l2.text())
        alfa = int(l3.text())
        a = float(l4.text())
        b = float(l9.text())
        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
        bevel = float(l8.text())
        helical = float(l10.text())
    except:
        FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n")

    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument("Involute Gear")
    involutegear = doc.addObject("Part::FeaturePython","InvoluteGear") #add object to document
    involutegear.Label = "Involute Gear"
    InvoluteGear(involutegear,N,m,alfa,a,b,width,c,j,bevel,helical)
    involutegear.ViewObject.Proxy=0
    App.ActiveDocument.recompute()
    Gui.SendMsgToActiveView("ViewFit")
    QtGui.qApp.restoreOverrideCursor()

def hide():
    dialog.hide()

if __name__ == "__main__": #feature will be generated after macro execution
#    makeInvoluteGear()
    QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)

    dialog = QtGui.QDialog()
    dialog.resize(200,450)
    dialog.setWindowTitle("Gear")
    la = QtGui.QVBoxLayout(dialog)
    t1 = QtGui.QLabel("Number of teeth (N)")
    la.addWidget(t1)
    la.addWidget(l1)
    t2 = QtGui.QLabel("module (m)")
    la.addWidget(t2)
    la.addWidget(l2)
    t3 = QtGui.QLabel("Pressure angle (alfa)")
    la.addWidget(t3)
    la.addWidget(l3)
    t4 = QtGui.QLabel("Addendum factor (* Module) (a)")   
    la.addWidget(t4)
    la.addWidget(l4)
    t9 = QtGui.QLabel("Dedendum factor (* Module) (b)")   
    la.addWidget(t9)
    la.addWidget(l9)
    t5 = QtGui.QLabel("Tooth clearance (c)")   
    la.addWidget(t5)
    la.addWidget(l5)
    t6 = QtGui.QLabel("Tooth lateral clearance (j)")   
    la.addWidget(t6)
    la.addWidget(l6)
    t7 = QtGui.QLabel("Gear width")
    la.addWidget(t7)
    la.addWidget(l7)
    t8 = QtGui.QLabel("Bevel Angle")
    la.addWidget(t8)
    la.addWidget(l8)
    t10 = QtGui.QLabel("Helical Angle")
    la.addWidget(t10)
    la.addWidget(l10)

    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()
kevinchentw
Posts: 4
Joined: Sun Sep 01, 2013 7:56 am

Re: A modified gear.py with bevel, helical angle

Post by kevinchentw »

ok, here is a new version.
It is fast, over 5 times speed than original one on my laptop, and some bug fixed.
And as before, you can change parameters after gear is setup.

Code: Select all

#Involute Gears Generation Script
#by Marcin Wanczyk (dj_who)
#(c) 2011 LGPL

# modified by Kevin Chen ( kevinttl@gmail.com ) Sep 1, 2013
# delevoped on Ubuntu

from __future__ import division # allows floating point division from integers
import FreeCAD, FreeCADGui, Part, Draft, math	# , MeshPart, Mesh
from FreeCAD import Base
from PyQt4 import QtGui,QtCore
App = FreeCAD
Gui = FreeCADGui

class InvoluteGear:
    def __init__(self, obj,prop_n,prop_m,prop_alfa,prop_a,prop_b,prop_width,prop_c,prop_j,prop_bevel,prop_helical):
        ''' Add the properties:  Number of Gears, Pressure Angle, Tooth Height Factor, Module, Tooth Clearance, Tooth Lateral Clearance, Width, Bevel Angle'''
        obj.addProperty("App::PropertyInteger","N","InvoluteGear","Number of Gears").N=prop_n
        obj.addProperty("App::PropertyFloat","m","InvoluteGear","Module").m=prop_m
        obj.addProperty("App::PropertyAngle","alfa","InvoluteGear","Pressure Angle").alfa=prop_alfa
        obj.addProperty("App::PropertyFloat","a","InvoluteGear","Addendum Factor (* Module)").a=prop_a
        obj.addProperty("App::PropertyFloat","b","InvoluteGear","Addendum Factor (* Module)").b=prop_b
        obj.addProperty("App::PropertyLength","width","InvoluteGear","Gear Width").width=prop_width
        obj.addProperty("App::PropertyLength","c","InvoluteGear","Tooth Clearance").c=prop_c
        obj.addProperty("App::PropertyLength","j","InvoluteGear","Tooth Lateral Clearance").j=prop_j
        obj.addProperty("App::PropertyAngle","bevel","InvoluteGear","Bevel Angle").bevel=prop_bevel
        obj.addProperty("App::PropertyAngle","helical","InvoluteGear","Helical Angle").helical=prop_helical
        obj.Proxy = self

    def onChanged(self, fp, prop):
        if prop == "N" or prop == "m" or prop == "alfa" or prop == "a" or prop == "b" or prop == "width" or prop == "c" or prop == "j" or prop == "bevel" or prop == "helical" : #if one of these is changed
            self.execute(fp)

    def execute(self, fp): #main part of script
        N = fp.N          
        alfa = fp.alfa
        m=fp.m      # module -> pitch diameter
        a = fp.a * m
        b = fp.b * m
        p = m * math.pi         # circle pitch
        c = fp.c* m     #standard value 0,1*m - 0,3*m
        j = fp.j * m     #standard value 0,015 - 0,04*m
        width = fp.width   #gear width
        bevel = fp.bevel
        helical = fp.helical
        if bevel > 70 or bevel < - 70 :
            FreeCAD.Console.PrintError("Bevel Angle should be in -70 ~ 70 degrees\n")
            return
        if bevel != 0 and helical != 0 :
            FreeCAD.Console.PrintError("Right now you cannot set both bevel and helical\n")
            return            
        # some consts
        dv_angle = math.radians(360 / N)
        
        #pitch diameter
        d=N*m

        #root diameter    
        df = d - 2 * b * m - 2 * c              #df=d-2hf where and hf=y*m+c 

        #addendum diameter    
        da = d + 2 * a * m                    #da=d+2ha where ha=y*m
    
        #base diameter for involute
        db=d * math.cos(math.radians(alfa))
 
        if df > db :
            # root is larger than base... not allowed!
            FreeCAD.Console.PrintError("Root diameter:" + str(df) + " is larger than Base diameter:" + str(db) + "\n")
            return

#        FreeCAD.Console.PrintError("pitch diameter=" + str(d) + ",base=" + str(db) + ",root=" + str(df) + "\n")
        rb = db / 2 # base radius
        if bevel != 0 :
            rbrate = 1 + width * math.tan(bevel) / d
            cal_top = True
#            FreeCAD.Console.PrintError("helical:" + str(helical) + "\n")
        elif helical != 0 :
            cal_top = True
        else :
            helical_rad = 0
            cal_top = False
        if cal_top :
            if helical != 0 :
                helical_rad = math.atan2(width * math.tan(math.radians(helical)),rb)
                helical = math.degrees(helical_rad)

        #Root circle
        rootCircleRadius = df / 2

        # solve addendum angle dirctly
        # r = rb * sqrt(1 + t * t)
        # so at addendum situation, r = da / 2
        # (d + 2 * y * m) / 2 = (d * cos(alfa) / 2) * sqrt(1 + t * t)
        # sqrt(1 + t * t) = (1 + 2 * y * m / d) / cos(alfa)
        # t * t = ((1 + 2 * y * m / d) / cos(alfa)) ^ 2 - 1
        # t = sqrt(((1 + 2 * y * m / d) / cos(alfa)) ^ 2 - 1)
        # pitch is a very important point! so....
        pt_angle_invaa = math.cos(math.radians(alfa))
        pt_angle = math.sqrt(1 / (pt_angle_invaa * pt_angle_invaa) - 1)
        # stop angle
        st_angle_aa = (1 + 2 * a * m / d) / pt_angle_invaa
        st_angle = math.sqrt(st_angle_aa * st_angle_aa - 1)
        # check angle (make sure addendum is reached!
        ck_angle = (st_angle - math.atan(st_angle)) * 2
        # angle steps -> around 5 pts / mm
        # divide into above pitch and below evenly as possible...
        n_ang_ap = int((da - d) * 2.5)
        if n_ang_ap < 2 :
            n_ang_ap = 2
        n_ang_bp = int((d - db) * 2.5)
        if n_ang_bp < 2 :
            n_ang_bp = 2
        n_angle = n_ang_ap + n_ang_bp - 1   # pitch point is dup

        #************ Calculating right sides of teeth
        #Involute of base circle
        involute=[]
        involutee=[]
        involutesav=[]
        if cal_top :
            involutetop = []    # if bevel or helical then we use this as top shape...
            involuteetop = []
            involutesavtop = []

#        FreeCAD.Console.PrintError("n_angle=" + str(n_angle) + ",st_angle=" + str(st_angle) + ",ck_angle=" + str(ck_angle) + "\n")
        for t in range(0,n_angle,1):
            if t < n_ang_bp :
                rang = pt_angle * t / (n_ang_bp - 1)
            else :
                rang = pt_angle + (st_angle - pt_angle) * (t - n_ang_bp + 1) / (n_ang_ap - 1)
            ang_cos = math.cos(rang)
            ang_sin = math.sin(rang)
            xang = (ang_cos + rang * ang_sin)
            yang = (ang_sin - rang * ang_cos)
            x = rb * xang
            y = rb * yang
            if cal_top :
                if bevel != 0 :
                    xtop0 = x * rbrate
                    ytop0 = y * rbrate
                else :
                    xtop0 = x
                    ytop0 = y
                if helical != 0 :
                    ang_cos = math.cos(helical_rad)
                    ang_sin = math.sin(helical_rad)
                    xtop = xtop0 * ang_cos - ytop0 * ang_sin
                    ytop = xtop0 * ang_sin + ytop0 * ang_cos
                else :
                    xtop = xtop0
                    ytop = ytop0
                involutetop.append(Part.Vertex(xtop,ytop,width).Point)
            if t == 0:
                rAngle = math.atan2(y,x)
            involute.append(Part.Vertex(x,y,0).Point)
#        FreeCAD.Console.PrintError("cal finish\n")

        #************ Drawing rigth sides of teeth
        involutesav.extend(involute)
        involutee.extend(involute)
        if cal_top :
            involutesavtop.extend(involutetop)
            involuteetop.extend(involutetop)

        startAngle = []
        stopAngle = []

        involuteL = [0]       # index 0 is useless
        if cal_top :
            involutetopL = [0]
#        FreeCAD.Console.PrintError("#0.1\n")
        sAngle = dv_angle
        for angle in range(1,N+1,1):
            ang_cos = math.cos(rAngle)
            ang_sin = math.sin(rAngle)
            involutee.insert(0,(rootCircleRadius *  ang_cos,rootCircleRadius * ang_sin,0))
            involuteshape = Part.makePolygon(involutee)
            involuteL.insert(angle,involuteshape)
            involutee=[]
            if cal_top :
                if bevel == 0 :
                    involuteetop.insert(0, (rootCircleRadius * math.cos(rAngle + helical_rad),rootCircleRadius * math.sin(rAngle + helical_rad),width))
                else :
                    rr = rootCircleRadius * rbrate
                    if helical == 0 :
                        involuteetop.insert(0, (rr * ang_cos,rr * ang_sin,width))
                    else :
                        involuteetop.insert(0, (rr * math.cos(rAngle + helical_rad),rr * math.sin(rAngle + helical_rad),width))
                involutetopshape = Part.makePolygon(involuteetop)
                involutetopL.insert(angle,involutetopshape)
                involuteetop = []
            stopAngle.append(sAngle)
            sang_cos = math.cos(sAngle)
            sang_sin = math.sin(sAngle)
            for num in range(0,n_angle,1):
                point=involute.pop()
                x = point.x * sang_cos - point.y * sang_sin
                y = point.x * sang_sin + point.y * sang_cos
                pointt = Part.Vertex(x,y,0).Point
                involutee.insert(0,pointt)
                if cal_top :
                    point = involutetop.pop()
                    xtop = point.x * sang_cos - point.y * sang_sin
                    ytop = point.x * sang_sin + point.y * sang_cos
                    pointt = Part.Vertex(xtop,ytop,width).Point
                    involuteetop.insert(0,pointt)
            rAngle = math.atan2(y,x)
            involute.extend(involutesav)
            if cal_top :
                involutetop.extend(involutesavtop)
            sAngle += dv_angle
        involutee=[]
        if cal_top :
            involuteetop = []
    
        #************ Calculating difference between tooth spacing on BaseCircle and PitchCircle

#        cut = involuteL[1].cut(pitchCircle)
        #    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.
        diff = involuteL[1].Vertexes[n_ang_bp].Point.y * 2
        anglediff = math.radians(180 / N) - dv_angle * (j / p)
        if anglediff < ck_angle :
            # addendum is not reachable
            FreeCAD.Console.PrintError("Tooth angle:" + str(anglediff) + " is less than Check angle:" + str(ck_angle) + "\nSo addendum (a) is not reachable\n")
            return
        anglediff += 2 * math.asin(diff / d)

        #************ Calculating left sides of teeth

#        FreeCAD.Console.PrintError("#0.2\n")
        #************ Inversing Involute
        for num in range(0,n_angle,1):
            point=involute.pop()
            pointt=Part.Vertex(point.x,- point.y,0).Point
            involutee.insert(0,pointt)
            if cal_top :
                point = involutetop.pop()
                pointt = Part.Vertex(point.x,- point.y,width).Point
                involuteetop.insert(0,pointt)
        involute.extend(involutee)
        involutee=[]
        if cal_top :
            involutetop.extend(involuteetop)
            involuteetop = []

        #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
#        FreeCAD.Console.PrintError("#0.3\n")
        ang_cos = math.cos(anglediff)
        ang_sin = math.sin(anglediff)
        if helical != 0 :
            tang_cos = math.cos(anglediff + 2 * helical_rad)
            tang_sin = math.sin(anglediff + 2 * helical_rad)
        for num in range(0,n_angle,1):
            point=involute.pop()
            x = point.x * ang_cos - point.y * ang_sin
            y = point.x * ang_sin + point.y * ang_cos
            pointt=Part.Vertex(x,y,0).Point
            involutee.insert(0,pointt)
            if cal_top :
                point = involutetop.pop()
                if helical != 0 :
                    xtop = point.x * tang_cos - point.y * tang_sin
                    ytop = point.x * tang_sin + point.y * tang_cos
                else :                    
                    xtop = point.x * ang_cos - point.y * ang_sin
                    ytop = point.x * ang_sin + point.y * ang_cos
                pointt = Part.Vertex(xtop,ytop,width).Point
                involuteetop.insert(0,pointt)
        rAngle = math.atan2(y,x)
        involute.extend(involutee)
        involutesav=[]
        involutesav.extend(involute)
        if cal_top :
            involutetop.extend(involuteetop)
            involutesavtop=[]
            involutesavtop.extend(involutetop)

        #************ Drawing left sides of teeth
        involuteR = [0]     # index 0 is useless
        if cal_top :
            involutetopR = [0]
#        FreeCAD.Console.PrintError("#0.4\n")
        sAngle = dv_angle
        for angle in range(1,N+1,1):
            ang_cos = math.cos(rAngle)
            ang_sin = math.sin(rAngle)
            involutee.insert(0,(rootCircleRadius * ang_cos,rootCircleRadius * ang_sin,0))        
            involuteshape = Part.makePolygon(involutee)
            involuteR.insert(angle,involuteshape)
            involutee=[]
            if cal_top :
                if bevel == 0 :
                    involuteetop.insert(0, (rootCircleRadius * math.cos(rAngle + helical_rad),rootCircleRadius * math.sin(rAngle + helical_rad),width))
                else :
                    rr = rootCircleRadius * rbrate
                    if helical == 0 :
                        involuteetop.insert(0, (rr * ang_cos,rr * ang_sin,width))
                    else :
                        involuteetop.insert(0, (rr * math.cos(rAngle + helical_rad),rr * math.sin(rAngle + helical_rad),width))
                involutetopshape = Part.makePolygon(involuteetop)
                involutetopR.insert(angle,involutetopshape)
                involuteetop = []                
            startAngle.append(rAngle)
            sang_cos = math.cos(sAngle)
            sang_sin = math.sin(sAngle)
            for num in range(0,n_angle,1):
                point = involute.pop()
                x = point.x * sang_cos - point.y * sang_sin
                y = point.x * sang_sin + point.y * sang_cos
                pointt=Part.Vertex(x,y,0).Point
                involutee.insert(0,pointt)
                if cal_top :
                    point = involutetop.pop()
                    xtop = point.x * sang_cos - point.y * sang_sin
                    ytop = point.x * sang_sin + point.y * sang_cos
                    pointt=Part.Vertex(xtop,ytop,width).Point
                    involuteetop.insert(0,pointt)
            rAngle = math.atan2(y,x)
            involute.extend(involutesav)
            if cal_top :
                involutetop.extend(involutesavtop)
            sAngle += dv_angle

        #************ Forming teeth

#        cutCircleE = Part.makeCircle(da)    # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be.
#        cutCircleW = Part.Wire(cutCircleE)
#        cutCircle = Part.Face(cutCircleW)

#        cutTool=cutCircle.cut(addendumCircle)
        #    cutshape=Part.show(cutTool)
#        FreeCAD.Console.PrintError("#1\n")

        if cal_top :
            if bevel == 0 :
                rrtop = rootCircleRadius
            else :
                rrtop = rootCircleRadius * rbrate
        for invNum in range(1,N+1,1):
#            FreeCAD.Console.PrintError("invNum=" + str(invNum) + "\n")
            invL = involuteL[invNum]
            invR = involuteR[invNum]
            pointL = invL.Vertexes.pop().Point
            pointR = invR.Vertexes.pop().Point
#            FreeCAD.Console.PrintError("points=" + str(pointL) + "," + str(pointR) + "\n")
            faceEdge=Part.makeLine(pointL,pointR)

            toothWhole = invL.fuse(invR)
            toothWhole=toothWhole.fuse(faceEdge)
            toothWire=Part.Wire(toothWhole.Edges)
 #           FreeCAD.Console.PrintError("#1.1d\n")
#            toothWire = Part.Wire([invL,invR,faceEdge])

            if invNum == 1:
                gearedge = toothWire
            else:
                gearedge = Part.Wire([gearedge,toothWire])

            toothArc = Part.makeCircle(rootCircleRadius,FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),math.degrees(startAngle[invNum - 1]),math.degrees(stopAngle[invNum - 1]))
#            FreeCAD.Console.PrintError("#1.3 " + str(rootCircleRadius) + "," +  str(math.degrees(startAngle[invNum - 1])) + "," + str(math.degrees(stopAngle[invNum - 1])) + "\n")
            gearedge = Part.Wire([gearedge,toothArc])
            if cal_top :
                invL = involutetopL[invNum]
                invR = involutetopR[invNum]
                pointL = invL.Vertexes.pop().Point
                pointR = invR.Vertexes.pop().Point
#                FreeCAD.Console.PrintError("points=" + str(pointL) + "," + str(pointR) + "\n")
                faceEdge=Part.makeLine(pointL,pointR)

                toothWhole = invL.fuse(invR)
                toothWhole=toothWhole.fuse(faceEdge)
                toothWire=Part.Wire(toothWhole.Edges)

#                FreeCAD.Console.PrintError("#1.3\n")
                if invNum == 1:
                    gearedgetop = toothWire
                else:
                    gearedgetop = Part.Wire([gearedgetop,toothWire])

                toothArc = Part.makeCircle(rrtop,FreeCAD.Vector(0,0,width),FreeCAD.Vector(0,0,1),math.degrees(startAngle[invNum - 1]) + helical,math.degrees(stopAngle[invNum - 1]) + helical)
#            FreeCAD.Console.PrintError("#1.3 " + str(rootCircleRadius) + "," +  str(math.degrees(startAngle[invNum - 1])) + "," + str(math.degrees(stopAngle[invNum - 1])) + "\n")
                gearedgetop = Part.Wire([gearedgetop,toothArc])
#            FreeCAD.Console.PrintError("#1.4\n")


#        FreeCAD.Console.PrintError("#2\n")
        gearShape=Part.Face(gearedge)
        if cal_top :
            gearTop = Part.Face(gearedgetop)
            if  helical != 0 :
                extloft = [gearShape, gearTop]
                for num in range(0,len(gearedge.Edges)) :
                    seg0 = Part.makeLine(gearedge.Edges[num].Vertexes[0].Point,gearedgetop.Edges[num].Vertexes[0].Point)
                    seg1 = Part.makeLine(gearedge.Edges[num].Vertexes[1].Point,gearedgetop.Edges[num].Vertexes[1].Point)
                    extloft.append(Part.makeFilledFace([seg0,gearedge.Edges[num],seg1,gearedgetop.Edges[num]]))
                extsh2 = Part.Shell(extloft)
            else :
                extloft = Part.makeLoft([gearedge,gearedgetop])
                extsh1 = extloft.fuse(gearShape)
                extsh2 = extsh1.fuse(gearTop)
            ext = Part.Solid(extsh2)
        else :
            ext = gearShape.extrude(Base.Vector(0,0,width))
        fp.Shape = ext      # result

default_n = 16
default_m = 1.0
default_alfa = 20
default_a = 1.1
default_b = 1.4
default_c = 0.1
default_j = 0.04
default_width = 6.0
default_bevel = 0.0
default_helical = 0.0

def makeInvoluteGear():
    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument()
    igearblock=doc.addObject("Part::FeaturePython","InvoluteGear") #add object to document
    igearblock.Label = "Involute Gear"
    InvoluteGear(igearblock,default_n,default_m,default_alfa,default_a,default_b,default_width,default_c,default_j,default_bevel,default_helical)
    igearblock.ViewObject.Proxy=0
    App.ActiveDocument.recompute()
    Gui.SendMsgToActiveView("ViewFit")


l1 = QtGui.QLineEdit()
l1.setText(str(default_n))
l2 = QtGui.QLineEdit()
l2.setText(str(default_m))
l3 = QtGui.QLineEdit()
l3.setText(str(default_alfa))
l4 = QtGui.QLineEdit()
l4.setText(str(default_a))
l5 = QtGui.QLineEdit()
l5.setText(str(default_c))
l6 = QtGui.QLineEdit()
l6.setText(str(default_j))
l7 = QtGui.QLineEdit()
l7.setText(str(default_width))
l8 = QtGui.QLineEdit()
l8.setText(str(default_bevel))
l9 = QtGui.QLineEdit()
l9.setText(str(default_b))
l10 = QtGui.QLineEdit()
l10.setText(str(default_helical))

def proceed():
    try:
        compute()
    except:
        hide()
        QtGui.qApp.restoreOverrideCursor()

def compute():    
    QtGui.qApp.restoreOverrideCursor()
    hide()
    try:
        N = int(l1.text())   
        m = float(l2.text())
        alfa = int(l3.text())
        a = float(l4.text())
        b = float(l9.text())
        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
        bevel = float(l8.text())
        helical = float(l10.text())
    except:
        FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n")

    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument("Involute Gear")
    involutegear = doc.addObject("Part::FeaturePython","InvoluteGear") #add object to document
    involutegear.Label = "Involute Gear"
    InvoluteGear(involutegear,N,m,alfa,a,b,width,c,j,bevel,helical)
    involutegear.ViewObject.Proxy=0
    App.ActiveDocument.recompute()
    Gui.SendMsgToActiveView("ViewFit")
    QtGui.qApp.restoreOverrideCursor()

def hide():
    dialog.hide()

if __name__ == "__main__": #feature will be generated after macro execution
#    makeInvoluteGear()
    QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)

    dialog = QtGui.QDialog()
    dialog.resize(200,450)
    dialog.setWindowTitle("Gear")
    la = QtGui.QVBoxLayout(dialog)
    t1 = QtGui.QLabel("Number of teeth (N)")
    la.addWidget(t1)
    la.addWidget(l1)
    t2 = QtGui.QLabel("module (m)")
    la.addWidget(t2)
    la.addWidget(l2)
    t3 = QtGui.QLabel("Pressure angle (alfa)")
    la.addWidget(t3)
    la.addWidget(l3)
    t4 = QtGui.QLabel("Addendum factor (* Module) (a)")   
    la.addWidget(t4)
    la.addWidget(l4)
    t9 = QtGui.QLabel("Dedendum factor (* Module) (b)")   
    la.addWidget(t9)
    la.addWidget(l9)
    t5 = QtGui.QLabel("Tooth clearance (c)")   
    la.addWidget(t5)
    la.addWidget(l5)
    t6 = QtGui.QLabel("Tooth lateral clearance (j)")   
    la.addWidget(t6)
    la.addWidget(l6)
    t7 = QtGui.QLabel("Gear width")
    la.addWidget(t7)
    la.addWidget(l7)
    t8 = QtGui.QLabel("Bevel Angle")
    la.addWidget(t8)
    la.addWidget(l8)
    t10 = QtGui.QLabel("Helical Angle")
    la.addWidget(t10)
    la.addWidget(l10)

    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()
User avatar
shoogen
Veteran
Posts: 2823
Joined: Thu Dec 01, 2011 5:24 pm

Re: A modified gear.py with bevel, helical angle

Post by shoogen »

I get "FeaturePython::onChanged (Involute Gear): Shape is not a shell" errors when the helix angle is greater than 5 deg.
And i've got problems to understand what makes the loft 'helical'. I use makePipeShell API with an auxiliary spine to create helical extrusions. If created the OpenSCADFeatures.Twist class to do that in a parametric fashion.
kevinchentw
Posts: 4
Joined: Sun Sep 01, 2013 7:56 am

Re: A modified gear.py with bevel, helical angle

Post by kevinchentw »

shoogen wrote:I get "FeaturePython::onChanged (Involute Gear): Shape is not a shell" errors when the helix angle is greater than 5 deg.
And i've got problems to understand what makes the loft 'helical'. I use makePipeShell API with an auxiliary spine to create helical extrusions. If created the OpenSCADFeatures.Twist class to do that in a parametric fashion.
Hi shoogen,
I've produced some helical gears with 45 deg and print them out via a 3d printer. I think may be it's the different version cause this problem. Mine is 0.14. Later I'll try on 0.13.
At first I tried loft to handle helical, but it obviously wrong, loft will pick nearest points to "loft", not actually wanted.
makePipeShell is a good idea, but I don't know how to use it. I tried Part, and it's instance, but no makePipeShell method...
Could you plz tell me how to do that? Thanx.

And for bevel gears, I cannot use the same way as in helical, it will complain about the same as you mentioned "shape is not a shell".. I don't know why.
Maybe I just mis-understand the definition of "shell"... Hope someone could talk about it.
razor49
Posts: 16
Joined: Sun Apr 27, 2014 10:46 am

Re: A modified gear.py with bevel, helical angle

Post by razor49 »

I tried this macro on lastest 0.14 but keep getting python error "Bevel Angle should be in -70 ~ 70 degrees"
User avatar
qboney
Posts: 1
Joined: Sat Jul 26, 2014 5:18 pm

Re: A modified gear.py with bevel, helical angle

Post by qboney »

Very good Job! Extremely well done!
I'm working on something very much like this, and your macro is the best out there to date, by far.

I still want more though :idea:

In my code, i want to add English and/or Module based addendum/dedendum, as well as fillet radius.
Also, i would like to produce a sketch, with options for External/Internal and Rack.

The pressure angle, face and flank radius are a proving a bit tricky for Module/Metric based gears.
For English face and flank radius, i'm pulling info from http://chestofbooks.com/home-improvemen ... 9xpYPmSySq
The English systems breaks gears down to 3 classes (based on tooth count), with Pressure Angle, addednum and dedendum different between the three classes.

In the photo, you can see the difference between TinHead, dj_who and what i'm currently working on.
Attachments
Gear Profiles
Gear Profiles
GearProfiles.jpg (40.97 KiB) Viewed 5912 times
nahshon
Posts: 225
Joined: Wed Jul 24, 2013 8:06 pm

Re: A modified gear.py with bevel, helical angle

Post by nahshon »

It's possible to create helical gears by sweeping an involute gear outline along a helix.

Calculating the angle is not trivial. Just remember that for a good match the gears must have the same module and presure angle. The helix pitch must me proportional to the number of teeth, and helixes must be of opposite direction.

Helix radius do not matter!

Image
cliffgi
Posts: 1
Joined: Sat May 31, 2014 1:49 am

Re: A modified gear.py with bevel, helical angle

Post by cliffgi »

A total tard (newbie) with both FreeCAD and Python, so I'm sure the issue is me. That being said, I created a new macro and have copied the modified gear.py into it. Saved and then ran then macro. A configuration screen pops permitting gear parameters to be set. Selecting 'Ok' closes configuration popup and displays the message: "Bevel angle shoud be between -70~70, and displays an empty view. I'm using FreeCAD v.14. Any clues out there as to how to resolve. Have not been able to find video or helpful brief.

thanks, cliffgi
User avatar
NormandC
Veteran
Posts: 18589
Joined: Sat Feb 06, 2010 9:52 pm
Location: Québec, Canada

Re: A modified gear.py with bevel, helical angle

Post by NormandC »

To other mods: wouldn't this topic be best placed in the Python scripting and macros forum rather than in Developers corner?
Post Reply