parent
c9c24cf0b6
commit
587f6b220b
|
@ -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
|
||||
|
|
30
src/Mod/PartDesign/fcgear/README
Normal file
30
src/Mod/PartDesign/fcgear/README
Normal file
|
@ -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.
|
0
src/Mod/PartDesign/fcgear/__init__.py
Normal file
0
src/Mod/PartDesign/fcgear/__init__.py
Normal file
108
src/Mod/PartDesign/fcgear/fcgear.py
Normal file
108
src/Mod/PartDesign/fcgear/fcgear.py
Normal file
|
@ -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
src/Mod/PartDesign/fcgear/fcgeardialog.py
Normal file
67
src/Mod/PartDesign/fcgear/fcgeardialog.py
Normal file
|
@ -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_()
|
251
src/Mod/PartDesign/fcgear/involute.py
Normal file
251
src/Mod/PartDesign/fcgear/involute.py
Normal file
|
@ -0,0 +1,251 @@
|
|||
# (c) 2014 David Douard <david.douard@gmail.com>
|
||||
# Based on https://github.com/attoparsec/inkscape-extensions.git
|
||||
# Based on gearUtils-03.js by Dr A.R.Collins
|
||||
# http://www.arc.id.au/gearDrawing.html
|
||||
#
|
||||
# Calculation of Bezier coefficients for
|
||||
# Higuchi et al. approximation to an involute.
|
||||
# ref: YNU Digital Eng Lab Memorandum 05-1
|
||||
#
|
||||
# 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
|
||||
|
||||
def CreateExternalGear(w, m, Z, phi, split=True):
|
||||
"""
|
||||
Create an external gear
|
||||
|
||||
w is wirebuilder object (in which the gear will be constructed)
|
||||
|
||||
if split is True, each profile of a teeth will consist in 2 Bezier
|
||||
curves of degree 3, otherwise it will be made of one Bezier curve
|
||||
of degree 4
|
||||
"""
|
||||
# ****** external gear specifications
|
||||
addendum = m # distance from pitch circle to tip circle
|
||||
dedendum = 1.25 * m # pitch circle to root, sets clearance
|
||||
clearance = dedendum - addendum
|
||||
|
||||
# Calculate radii
|
||||
Rpitch = Z * m / 2 # pitch circle radius
|
||||
Rb = Rpitch*cos(phi * pi / 180) # base circle radius
|
||||
Ra = Rpitch + addendum # tip (addendum) circle radius
|
||||
Rroot = Rpitch - dedendum # root circle radius
|
||||
fRad = 1.5 * clearance # fillet radius, max 1.5*clearance
|
||||
Rf = sqrt((Rroot + fRad)**2 - fRad**2) # radius at top of fillet
|
||||
if (Rb < Rf):
|
||||
Rf = Rroot + clearance
|
||||
|
||||
# ****** calculate angles (all in radians)
|
||||
pitchAngle = 2 * pi / Z # angle subtended by whole tooth (rads)
|
||||
baseToPitchAngle = genInvolutePolar(Rb, Rpitch)
|
||||
pitchToFilletAngle = baseToPitchAngle # profile starts at base circle
|
||||
if (Rf > Rb): # start profile at top of fillet (if its greater)
|
||||
pitchToFilletAngle -= genInvolutePolar(Rb, Rf)
|
||||
|
||||
filletAngle = atan(fRad / (fRad + Rroot)) # radians
|
||||
|
||||
# ****** generate Higuchi involute approximation
|
||||
fe = 1 # fraction of profile length at end of approx
|
||||
fs = 0.01 # fraction of length offset from base to avoid singularity
|
||||
if (Rf > Rb):
|
||||
fs = (Rf**2 - Rb**2) / (Ra**2 - Rb**2) # offset start to top of fillet
|
||||
|
||||
if split:
|
||||
# approximate in 2 sections, split 25% along the involute
|
||||
fm = fs + (fe - fs) / 4 # fraction of length at junction (25% along profile)
|
||||
dedInv = BezCoeffs(m, Z, phi, 3, fs, fm)
|
||||
addInv = BezCoeffs(m, Z, phi, 3, fm, fe)
|
||||
|
||||
# join the 2 sets of coeffs (skip duplicate mid point)
|
||||
inv = dedInv + addInv[1:]
|
||||
else:
|
||||
inv = BezCoeffs(m, Z, phi, 4, fs, fe)
|
||||
|
||||
# create the back profile of tooth (mirror image)
|
||||
invR = []
|
||||
for i, pt in enumerate(inv):
|
||||
# rotate all points to put pitch point at y = 0
|
||||
ptx, pty = inv[i] = rotate(pt, -baseToPitchAngle - pitchAngle / 4)
|
||||
# generate the back of tooth profile nodes, mirror coords in X axis
|
||||
invR.append((ptx, -pty))
|
||||
|
||||
# ****** calculate section junction points R=back of tooth, Next=front of next tooth)
|
||||
fillet = toCartesian(Rf, -pitchAngle / 4 - pitchToFilletAngle) # top of fillet
|
||||
filletR = [fillet[0], -fillet[1]] # flip to make same point on back of tooth
|
||||
rootR = toCartesian(Rroot, pitchAngle / 4 + pitchToFilletAngle + filletAngle)
|
||||
rootNext = toCartesian(Rroot, 3 * pitchAngle / 4 - pitchToFilletAngle - filletAngle)
|
||||
filletNext = rotate(fillet, pitchAngle) # top of fillet, front of next tooth
|
||||
|
||||
# Build the shapes using FreeCAD.Part
|
||||
t_inc = 2.0 * pi / float(Z)
|
||||
thetas = [(x * t_inc) for x in range(Z)]
|
||||
|
||||
w.move(fillet) # start at top of fillet
|
||||
|
||||
for theta in thetas:
|
||||
w.theta = theta
|
||||
if (Rf < Rb):
|
||||
w.line(inv[0]) # line from fillet up to base circle
|
||||
|
||||
if split:
|
||||
w.curve(inv[1], inv[2], inv[3])
|
||||
w.curve(inv[4], inv[5], inv[6])
|
||||
w.arc(invR[6], Ra, 1) # arc across addendum circle
|
||||
w.curve(invR[5], invR[4], invR[3])
|
||||
w.curve(invR[2], invR[1], invR[0])
|
||||
else:
|
||||
w.curve(*inv[1:])
|
||||
w.arc(invR[-1], Ra, 1) # arc across addendum circle
|
||||
w.curve(*invR[-2::-1])
|
||||
|
||||
if (Rf < Rb):
|
||||
w.line(filletR) # line down to topof fillet
|
||||
|
||||
if (rootNext[1] > rootR[1]): # is there a section of root circle between fillets?
|
||||
w.arc(rootR, fRad, 0) # back fillet
|
||||
w.arc(rootNext, Rroot, 1) # root circle arc
|
||||
|
||||
w.arc(filletNext, fRad, 0)
|
||||
|
||||
w.close()
|
||||
return w
|
||||
|
||||
|
||||
|
||||
def genInvolutePolar(Rb, R):
|
||||
"""returns the involute angle as function of radius R.
|
||||
Rb = base circle radius
|
||||
"""
|
||||
return (sqrt(R*R - Rb*Rb) / Rb) - acos(Rb / R)
|
||||
|
||||
|
||||
def rotate(pt, rads):
|
||||
"rotate pt by rads radians about origin"
|
||||
sinA = sin(rads)
|
||||
cosA = cos(rads)
|
||||
return (pt[0] * cosA - pt[1] * sinA,
|
||||
pt[0] * sinA + pt[1] * cosA)
|
||||
|
||||
|
||||
|
||||
def toCartesian(radius, angle):
|
||||
"convert polar coords to cartesian"
|
||||
return [radius * cos(angle), radius * sin(angle)]
|
||||
|
||||
|
||||
def chebyExpnCoeffs(j, func):
|
||||
N = 50 # a suitably large number N>>p
|
||||
c = 0
|
||||
for k in xrange(1, N + 1):
|
||||
c += func(cos(pi * (k - 0.5) / N)) * cos(pi * j * (k - 0.5) / N)
|
||||
return 2 *c / N
|
||||
|
||||
|
||||
def chebyPolyCoeffs(p, func):
|
||||
coeffs = [0]*(p+1)
|
||||
fnCoeff = []
|
||||
T = [coeffs[:] for i in range(p+1)]
|
||||
T[0][0] = 1
|
||||
T[1][1] = 1
|
||||
# now generate the Chebyshev polynomial coefficient using
|
||||
# formula T(k+1) = 2xT(k) - T(k-1) which yields
|
||||
# T = [ [ 1, 0, 0, 0, 0, 0], # T0(x) = +1
|
||||
# [ 0, 1, 0, 0, 0, 0], # T1(x) = 0 +x
|
||||
# [-1, 0, 2, 0, 0, 0], # T2(x) = -1 0 +2xx
|
||||
# [ 0, -3, 0, 4, 0, 0], # T3(x) = 0 -3x 0 +4xxx
|
||||
# [ 1, 0, -8, 0, 8, 0], # T4(x) = +1 0 -8xx 0 +8xxxx
|
||||
# [ 0, 5, 0,-20, 0, 16], # T5(x) = 0 5x 0 -20xxx 0 +16xxxxx
|
||||
# ... ]
|
||||
|
||||
for k in xrange(1, p):
|
||||
for j in xrange(len(T[k]) - 1):
|
||||
T[k + 1][j + 1] = 2 * T[k][j]
|
||||
for j in xrange(len(T[k - 1])):
|
||||
T[k + 1][j] -= T[k - 1][j]
|
||||
|
||||
# convert the chebyshev function series into a simple polynomial
|
||||
# and collect like terms, out T polynomial coefficients
|
||||
for k in xrange(p + 1):
|
||||
fnCoeff.append(chebyExpnCoeffs(k, func))
|
||||
|
||||
for k in xrange(p + 1):
|
||||
for pwr in xrange(p + 1):
|
||||
coeffs[pwr] += fnCoeff[k] * T[k][pwr]
|
||||
|
||||
coeffs[0] -= fnCoeff[0] / 2 # fix the 0th coeff
|
||||
return coeffs
|
||||
|
||||
|
||||
def binom(n, k):
|
||||
coeff = 1
|
||||
for i in xrange(n - k + 1, n + 1):
|
||||
coeff *= i
|
||||
|
||||
for i in xrange(1, k + 1):
|
||||
coeff /= i
|
||||
|
||||
return coeff
|
||||
|
||||
|
||||
def bezCoeff(i, p, polyCoeffs):
|
||||
'''generate the polynomial coeffs in one go'''
|
||||
return sum(binom(i, j) * polyCoeffs[j] / binom(p, j) for j in range(i+1))
|
||||
|
||||
|
||||
# Parameters:
|
||||
# module - sets the size of teeth (see gear design texts)
|
||||
# numTeeth - number of teeth on the gear
|
||||
# pressure angle - angle in degrees, usually 14.5 or 20
|
||||
# order - the order of the Bezier curve to be fitted [3, 4, 5, ..]
|
||||
# fstart - fraction of distance along tooth profile to start
|
||||
# fstop - fraction of distance along profile to stop
|
||||
def BezCoeffs(module, numTeeth, pressureAngle, order, fstart, fstop):
|
||||
Rpitch = module * numTeeth / 2 # pitch circle radius
|
||||
phi = pressureAngle # pressure angle
|
||||
Rb = Rpitch * cos(phi * pi / 180) # base circle radius
|
||||
Ra = Rpitch + module # addendum radius (outer radius)
|
||||
ta = sqrt(Ra * Ra - Rb * Rb) / Rb # involute angle at addendum
|
||||
te = sqrt(fstop) * ta # involute angle, theta, at end of approx
|
||||
ts = sqrt(fstart) * ta # involute angle, theta, at start of approx
|
||||
p = order # order of Bezier approximation
|
||||
|
||||
def involuteXbez(t):
|
||||
"Equation of involute using the Bezier parameter t as variable"
|
||||
# map t (0 <= t <= 1) onto x (where -1 <= x <= 1)
|
||||
x = t * 2 - 1
|
||||
# map theta (where ts <= theta <= te) from x (-1 <=x <= 1)
|
||||
theta = x * (te - ts) / 2 + (ts + te) / 2
|
||||
return Rb * (cos(theta) + theta * sin(theta))
|
||||
|
||||
def involuteYbez(t):
|
||||
"Equation of involute using the Bezier parameter t as variable"
|
||||
# map t (0 <= t <= 1) onto x (where -1 <= x <= 1)
|
||||
x = t * 2 - 1
|
||||
# map theta (where ts <= theta <= te) from x (-1 <=x <= 1)
|
||||
theta = x * (te - ts) / 2 + (ts + te) / 2
|
||||
return Rb * (sin(theta) - theta * cos(theta))
|
||||
|
||||
# calc Bezier coeffs
|
||||
bzCoeffs = []
|
||||
polyCoeffsX = chebyPolyCoeffs(p, involuteXbez)
|
||||
polyCoeffsY = chebyPolyCoeffs(p, involuteYbez)
|
||||
for i in xrange(p + 1):
|
||||
bx = bezCoeff(i, p, polyCoeffsX)
|
||||
by = bezCoeff(i, p, polyCoeffsY)
|
||||
bzCoeffs.append((bx, by))
|
||||
return bzCoeffs
|
||||
|
71
src/Mod/PartDesign/fcgear/svggear.py
Normal file
71
src/Mod/PartDesign/fcgear/svggear.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
# (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
|
||||
|
||||
import itertools
|
||||
from math import cos, sin
|
||||
from involute import CreateExternalGear, rotate
|
||||
|
||||
def makeGear(m, Z, angle):
|
||||
w = SVGWireBuilder()
|
||||
CreateExternalGear(w, m, Z, angle)
|
||||
return '\n'.join(w.svg)
|
||||
|
||||
class SVGWireBuilder(object):
|
||||
def __init__(self):
|
||||
self.theta = 0.0
|
||||
self.pos = None
|
||||
self.svg = []
|
||||
|
||||
def move(self, p):
|
||||
p = rotate(p, self.theta)
|
||||
self.svg.append('M %s,%s' % (p[0], p[1]))
|
||||
self.pos = p
|
||||
|
||||
def line(self, p):
|
||||
p = rotate(p, self.theta)
|
||||
self.svg.append('L %s,%s' % (p[0], p[1]))
|
||||
self.pos = p
|
||||
|
||||
def arc(self, p, r, sweep):
|
||||
p = rotate(p, self.theta)
|
||||
self.svg.append('A %s,%s 0,0,%s %s,%s' % (r, r, str(sweep), p[0], p[1]))
|
||||
self.pos = p
|
||||
|
||||
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) )
|
||||
"""
|
||||
assert len(points) == 3
|
||||
points = [rotate(p, self.theta) for p in points]
|
||||
self.svg.append('C %s,%s %s,%s %s,%s' % tuple(itertools.chain(*points)))
|
||||
self.pos = points[-1]
|
||||
|
||||
def close(self):
|
||||
self.svg.append('Z')
|
||||
|
||||
if __name__ == '__main__':
|
||||
from optparse import OptionParser
|
||||
p = OptionParser()
|
||||
p.add_option('-a', '--angle', help='pressure angle',
|
||||
dest='angle', default=20)
|
||||
opts, args = p.parse_args()
|
||||
if len(args) != 2:
|
||||
p.error()
|
||||
m, Z = [float(v) for v in args]
|
||||
print makeGear(m, int(Z), float(opts.angle))
|
||||
|
Loading…
Reference in New Issue
Block a user