import FreeCAD, FreeCADGui, Part, Draft, MeshPart, Mesh
from PyQt4 import QtGui,QtCore
from math import *
App=FreeCAD
Gui=FreeCADGui


#QT callbacks
def proceed():
    try:
        G = Gear()
        G.draw()
        
    except:
        hide()
        QtGui.qApp.restoreOverrideCursor()

def hide():
    dialog.hide()


#Utility funcs
def involute_intersect_angle(Rb, R):
    Rb, R = float(Rb), float(R)
    return (sqrt(R**2 - Rb**2) / (Rb)) - (acos(Rb / R))

def point_on_circle(radius, angle):
    x = radius * cos(angle)
    y = radius * sin(angle)
    return (x, y)

def points_to_sketch(p):
        doc=App.activeDocument()
        poly = list()
        print len(poly)
        for point in p:
                v=App.Vector(float(point[0]),float(point[1]),0.0)
                poly.append(v)        
        last = App.Vector(float(p[0][0]),float(p[0][1]),0.0)
        poly.append(last)
        p=doc.addObject("Part::Polygon","Polygon") 
        p.Nodes=poly 
        doc.recompute()

# Gear class from inkscape

class Gear():
    def __init__(self):
    
        self.teeth_input = int(l1.text())
        self.pitch_input = float(l2.text())
        self.angle_input = float(l3.text())

    def draw(self):

        teeth = self.teeth_input
        pitch = self.pitch_input
        angle = self.angle_input  # Angle of tangent to tooth at circular pitch wrt radial line.

        # print >>sys.stderr, "Teeth: %s\n"        % teeth

        two_pi = 2.0 * pi

        # Pitch (circular pitch): Length of the arc from one tooth to the next)
        # Pitch diameter: Diameter of pitch circle.
        pitch_diameter = float( teeth ) * pitch / pi
        pitch_radius   = pitch_diameter / 2.0

        # Base Circle
        base_diameter = pitch_diameter * cos( radians( angle ) )
        base_radius   = base_diameter / 2.0

        # Diametrial pitch: Number of teeth per unit length.
        pitch_diametrial = float( teeth )/ pitch_diameter

        # Addendum: Radial distance from pitch circle to outside circle.
        addendum = 1.0 / pitch_diametrial

        # Outer Circle
        outer_radius = pitch_radius + addendum
        outer_diameter = outer_radius * 2.0

        # Tooth thickness: Tooth width along pitch circle.
        tooth  = ( pi * pitch_diameter ) / ( 2.0 * float( teeth ) )

        # Undercut?
        undercut = (2.0 / ( sin( radians( angle ) ) ** 2))
        needs_undercut = teeth < undercut


        # Clearance: Radial distance between top of tooth on one gear to bottom of gap on another.
        clearance = 0.0

        # Dedendum: Radial distance from pitch circle to root diameter.
        dedendum = addendum + clearance

        # Root diameter: Diameter of bottom of tooth spaces. 
        root_radius =  pitch_radius - dedendum
        root_diameter = root_radius * 2.0

        half_thick_angle = two_pi / (4.0 * float( teeth ) )
        pitch_to_base_angle  = involute_intersect_angle( base_radius, pitch_radius )
        pitch_to_outer_angle = involute_intersect_angle( base_radius, outer_radius ) - pitch_to_base_angle

        centers = [(x * two_pi / float( teeth) ) for x in range( teeth ) ]

        points = []

        for c in centers:

            # Angles
            pitch1 = c - half_thick_angle
            base1  = pitch1 - pitch_to_base_angle
            outer1 = pitch1 + pitch_to_outer_angle

            pitch2 = c + half_thick_angle
            base2  = pitch2 + pitch_to_base_angle
            outer2 = pitch2 - pitch_to_outer_angle

            # Points
            b1 = point_on_circle( base_radius,  base1  )
            p1 = point_on_circle( pitch_radius, pitch1 )
            o1 = point_on_circle( outer_radius, outer1 )

            b2 = point_on_circle( base_radius,  base2  )
            p2 = point_on_circle( pitch_radius, pitch2 )
            o2 = point_on_circle( outer_radius, outer2 )

            if root_radius > base_radius:
                pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius )
                root1 = pitch1 - pitch_to_root_angle
                root2 = pitch2 + pitch_to_root_angle
                r1 = point_on_circle(root_radius, root1)
                r2 = point_on_circle(root_radius, root2)
                p_tmp = [r1,p1,o1,o2,p2,r2]
            else:
                r1 = point_on_circle(root_radius, base1)
                r2 = point_on_circle(root_radius, base2)
                p_tmp = [r1,b1,p1,o1,o2,p2,b2,r2]

            points.extend( p_tmp )

        path = points_to_sketch( points )

# show dialog
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)
t7 = QtGui.QLabel("Gear width")   
la.addWidget(t7)
l7 = QtGui.QLineEdit()
l7.setText("6.0")
la.addWidget(l7)

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


