Skip to content

Commit

Permalink
add fcgear
Browse files Browse the repository at this point in the history
  • Loading branch information
jriegel committed Feb 16, 2014
1 parent c9c24cf commit 587f6b2
Show file tree
Hide file tree
Showing 7 changed files with 539 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/Mod/PartDesign/CMakeLists.txt
Expand Up @@ -27,6 +27,18 @@ INSTALL(
Scripts/Spring.py
DESTINATION
Mod/PartDesign/Scripts
)

INSTALL(
FILES
fcgear/__init__.py
fcgear/fcgear.py
fcgear/fcgeardialog.py
fcgear/involute.py
fcgear/svggear.py
DESTINATION
Mod/PartDesign/fcgear

)

SET(WizardShaft_SRCS
Expand Down
30 changes: 30 additions & 0 deletions src/Mod/PartDesign/fcgear/README
@@ -0,0 +1,30 @@
================================================
FCGear: an Involute Gear Generator for FreeCAD
================================================

This is a simple gear generation tool usable in FreeCAD. The tooth
profiles are approximations of the ideal involutes by Bezier curves,
according the paper:

Approximation of Involute Curves for CAD-System Processing
Higuchi et al. approximation to an involute.
ref: YNU Digital Eng Lab Memorandum 05-1
http://maekawalab-ynu.com/papers.html

This code is based on the JavaScript implementation of the published
method provided by A.R. Collins in his gearUtils.js tool:

Based on gearUtils-03.js by Dr A.R.Collins
Latest version: <www.arc.id.au/gearDrawing.html>

Also took inspirations from the Inkscape extension provided by Matthew
Dockrey on

https://github.com/attoparsec/inkscape-extensions.git

The simplest way to use it is to copy the example macro file
gear.FCMacro to ~/.FreeCAD/ (make sure the fcgear directory is in the
FreeCAD's Python path).

Copyright 2014 David Douard <david.douard@gmail.com>.
Distributed under the LGPL licence.
Empty file.
108 changes: 108 additions & 0 deletions src/Mod/PartDesign/fcgear/fcgear.py
@@ -0,0 +1,108 @@
# (c) 2014 David Douard <david.douard@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (LGPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# for detail see the LICENCE text file.
#
# FCGear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with FCGear; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307

from math import cos, sin, pi, acos, asin, atan, sqrt

import FreeCAD, FreeCADGui, Part
from FreeCAD import Base, Console
import involute
reload(involute)
rotate = involute.rotate


def makeGear(m, Z, angle, split=True):
if FreeCAD.ActiveDocument is None:
FreeCAD.newDocument("Gear")
doc = FreeCAD.ActiveDocument
w = FCWireBuilder()
involute.CreateExternalGear(w, m, Z, angle, split)
gearw = Part.Wire([o.toShape() for o in w.wire])
gear = doc.addObject("Part::Feature", "Gear")
gear.Shape = gearw
return gear


class FCWireBuilder(object):
"""A helper class to prepare a Part.Wire object"""
def __init__(self):
self.pos = None
self.theta = 0.0
self.wire = []

def move(self, p):
"""set current position"""
self.pos = Base.Vector(*p)

def line(self, p):
"""Add a segment between self.pos and p"""
p = rotate(p, self.theta)
end = Base.Vector(*p)
self.wire.append(Part.Line(self.pos, end))
self.pos = end

def arc(self, p, r, sweep):
""""Add an arc from self.pos to p which radius is r
sweep (0 or 1) determine the orientation of the arc
"""
p = rotate(p, self.theta)
end = Base.Vector(*p)
mid = Base.Vector(*(midpoints(p, self.pos, r)[sweep]))
self.wire.append(Part.Arc(self.pos, mid, end))
self.pos = end

def curve(self, *points):
"""Add a Bezier curve from self.pos to points[-1]
every other points are the control points of the Bezier curve (which
will thus be of degree len(points) )
"""
points = [Base.Vector(*rotate(p, self.theta)) for p in points]
bz = Part.BezierCurve()
bz.setPoles([self.pos] + points)
self.wire.append(bz)
self.pos = points[-1]

def close(self):
pass

def midpoints(p1, p2, r):
"""A very ugly function that returns the midpoint of a p1 and p2
on the circle which radius is r and which pass throught p1 and
p2
Return the 2 possible solutions
"""
vx, vy = p2[0]-p1[0], p2[1]-p1[1]
b = (vx**2 + vy**2)**.5
v = (vx/b, vy/b)
cosA = b**2 / (2*b*r)
A = acos(cosA)

vx, vy = rotate(v, A)
c1 = (p1[0]+r*vx, p1[1]+r*vy)
m1x, m1y = ((p1[0]+p2[0])/2 - c1[0], (p1[1]+p2[1])/2 - c1[1])
dm1 = (m1x**2+m1y**2)**.5
m1x, m1y = (c1[0] + r*m1x/dm1, c1[1] + r*m1y/dm1)
m1 = (m1x, m1y)

vx, vy = rotate(v, -A)
c2 = (p1[0]+r*vx, p1[1]+r*vy)
m2x, m2y = ((p1[0]+p2[0])/2 - c2[0], (p1[1]+p2[1])/2 - c2[1])
dm2 = (m2x**2+m2y**2)**.5
m2x, m2y = (c2[0] + r*m2x/dm2, c2[1] + r*m2y/dm2)
m2 = (m2x, m2y)

return m1, m2
67 changes: 67 additions & 0 deletions src/Mod/PartDesign/fcgear/fcgeardialog.py
@@ -0,0 +1,67 @@
# (c) 2014 David Douard <david.douard@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (LGPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# for detail see the LICENCE text file.
#
# FCGear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with FCGear; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307

from PyQt4 import QtGui as qt
import fcgear
import FreeCAD, FreeCADGui

class GearCreationFrame(qt.QFrame):
def __init__(self, parent=None):
super(GearCreationFrame, self).__init__(parent)
self.Z = qt.QSpinBox(value=26)
self.m = qt.QDoubleSpinBox(value=2.5)
self.angle = qt.QDoubleSpinBox(value=20)
self.split = qt.QComboBox()
self.split.addItems(['2x3', '1x4'])
l = qt.QFormLayout(self)
l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
l.addRow('Number of teeth:', self.Z)
l.addRow('Modules (mm):', self.m)
l.addRow('Pressure angle:', self.angle)
l.addRow('Number of curves:', self.split)

class GearDialog(qt.QDialog):
def __init__(self, parent=None):
super(GearDialog, self).__init__(parent)
self.gc = GearCreationFrame()

btns = qt.QDialogButtonBox.Ok | qt.QDialogButtonBox.Cancel
buttonBox = qt.QDialogButtonBox(btns,
accepted=self.accept,
rejected=self.reject)
l = qt.QVBoxLayout(self)
l.addWidget(self.gc)
l.addWidget(buttonBox)
self.setWindowTitle('Gear cration dialog')

def accept(self):
if FreeCAD.ActiveDocument is None:
FreeCAD.newDocument("Gear")

gear = fcgear.makeGear(self.gc.m.value(),
self.gc.Z.value(),
self.gc.angle.value(),
not self.gc.split.currentIndex())
FreeCADGui.SendMsgToActiveView("ViewFit")
return super(GearDialog, self).accept()


if __name__ == '__main__':
a = qt.QApplication([])
w = GearDialog()
w.show()
a.exec_()

0 comments on commit 587f6b2

Please sign in to comment.