19 Commits
py2 ... 0.3

Author SHA1 Message Date
looooo
8db1141a57 version 2015-06-26 13:37:19 +02:00
looooo
b1e1d7467d icons 2015-06-26 13:36:45 +02:00
looooo
c44738a6f6 working 2015-06-26 13:36:25 +02:00
looooo
f3ec22f524 0.2.6 2015-06-26 12:11:07 +02:00
looooo
1b46e4b39a 0.2.6 2015-06-26 11:56:50 +02:00
looooo
62909966f2 manifest 2015-06-26 11:14:11 +02:00
looooo
ecc42b23ca umbenannt 2015-06-25 11:49:07 +02:00
looooo
00d814e8bd schas 2015-06-25 11:44:11 +02:00
looooo
ab50047800 es nervt 2015-06-25 11:42:09 +02:00
looooo
23f8dbaf19 manifest.in 2015-06-25 11:26:44 +02:00
looooo
a511a2835d setup version 2015-06-25 11:01:15 +02:00
looooo
ff1fe8bdb3 bla 2015-06-25 10:32:24 +02:00
looooo
f68653d442 setup 2015-06-25 10:27:36 +02:00
looooo
4c7f5410b0 not working 2015-06-25 09:45:12 +02:00
looooo
6abbeb1fef tag nr 2015-06-25 08:42:39 +02:00
looooo
146767418a integration 2015-06-25 08:37:02 +02:00
looooo
97df5a8077 gui stuff 2015-06-23 06:48:00 +02:00
looooo
5fc6b813e3 way towards pip 2015-06-19 21:38:22 +02:00
looooo
25aafca940 way to pip 2015-06-19 19:04:08 +02:00
39 changed files with 1316 additions and 2378 deletions

View File

@@ -1 +1 @@
recursive-include freecad/gears/icons * recursive-include freecad_gear/freecad/icons *

View File

@@ -1,82 +1,14 @@
# A Gear module for FreeCAD a gearmodule for freecad
[![Liberapay](http://img.shields.io/liberapay/patrons/looooo.svg?logo=liberapay)](https://liberapay.com/looooo/donate)
## Requirements
FreeCAD > v0.16
# Screenshots
![gear](examples/spiral.png)
![gear1](examples/animated_spiral.gif)
## Supported gear-types
### Cylindric Involute
#### Shifting
#### Helical
#### Double Helical
#### Undercut
### Involute Rack
### Cylindric Cycloid
#### Helical
#### Double Helical
### Spherical Involute Bevel-Gear
#### Spiral
### Crown-Gear
--------------------------- ---------------------------
## Installation * install:
* git clone https://github.com/looooo/FCGear.git
* link or copy the FCgear/gear into /freecad/Mod (sudo ln -s (path_to_FCGear)/gear (path_to_freecad)/Mod
### Addon Manger
Starting from v0.17 it's possible to use the built-in FreeCAD [Addon Manager](https://github.com/FreeCAD/FreeCAD-addons#1-builtin-addon-manager)
located in the `Tools` > `Addon Manager` dropdown menu.
### pip * create a gear:
* open freecad
`pip install https://github.com/looooo/FCGear/archive/master.tar.gz` * go to the gear workbench
* create new document
**Important note:** Most systems have multiple versions of python installed. Make sure the `pip` you're using is used by FreeCAD as well. * create a gear (click on gear symbol)
* change parameters
## Usage
### Create a gear manually
* Open freecad
* Switch to the gear workbench
* Create new document
* Create a gear (click on a gear symbol in the toolbar)
* Change the gear parameters
## Scripted gears
Use the power of python to automate your gear modeling:
```python
import FreeCAD as App
import freecad.gears.commands
gear = freecad.gears.commands.CreateInvoluteGear.create()
gear.teeth = 20
gear.beta = 20
gear.height = 10
gear.double_helix = True
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
```
## References
* Elements of Metric Gear Technology ([PDF](http://qtcgears.com/tools/catalogs/PDF_Q420/Tech.pdf))
### FreeCAD Forum threads
These are forum threads where FreeCAD Gears has been discussed. If you want to give Feedback
or report a bug please use the below threads. Please make sure that the report hasn't been reported already
by browsing this repositories [issue queue](https://github.com/looooo/freecad.gears/issues).
* "CONTINUED: involute gear generator preview !" ([thread](https://forum.freecadweb.org/viewtopic.php?f=10&t=4829))
* "Bevel gear - module/script/tutorial" ([thread](https://forum.freecadweb.org/viewtopic.php?f=3&t=12878))
* "Gears in FreeCAD: FC Gear" ([thread](https://forum.freecadweb.org/viewtopic.php?f=24&t=27381))
* "FC Gears: Feedback thread" ([thread](https://forum.freecadweb.org/viewtopic.php?f=8&t=27626))
# License
GNU General Public License v2.0

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

View File

@@ -1,32 +0,0 @@
# script for bevel-gear animation
from PySide import QtGui, QtCore
import FreeCADGui as Gui
import numpy as np
import imageio
doc = App.ActiveDocument
g2 = doc.Common
g1 = doc.Common001
timer = QtCore.QTimer()
def make_pics():
n = 30
for i in range(n):
phi = np.pi * 2 / 30 / n
g1.Placement.Rotation.Angle += phi * 2
g2.Placement.Rotation.Angle -= phi
Gui.activeDocument().activeView().saveImage('/home/lo/Schreibtisch/animated_gear/gear_{}.png'.format(i) ,300,300,'Current')
def make_animated_gif():
def update(*args):
print("time")
delta_phi = 0.005
g1.Placement.Rotation.Angle += delta_phi * 2
g2.Placement.Rotation.Angle -= delta_phi
timer.timeout.connect(update)
timer.start()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -1 +0,0 @@
__path__ = __import__('pkgutil').extend_path(__path__, __name__)

View File

@@ -1,2 +0,0 @@
import pygears
__version__ = pygears.__version__

View File

@@ -1,94 +0,0 @@
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import os
import FreeCAD
import FreeCADGui as Gui
from .features import ViewProviderGear, involute_gear, involute_gear_rack
from .features import cycloide_gear, bevel_gear, crown_gear
class BaseCommand(object):
NAME = ""
GEAR_FUNCTION = None
ICONDIR = os.path.join(os.path.dirname(__file__), "icons")
def __init__(self):
pass
def IsActive(self):
if FreeCAD.ActiveDocument is None:
return False
else:
return True
def Activated(self):
Gui.doCommandGui("import freecad.gears.commands")
Gui.doCommandGui("freecad.gears.commands.{}.create()".format(self.__class__.__name__))
FreeCAD.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
@classmethod
def create(cls):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", cls.NAME)
cls.GEAR_FUNCTION(obj)
ViewProviderGear(obj.ViewObject)
return obj
def GetResources(self):
return {'Pixmap': self.Pixmap,
'MenuText': self.MenuText,
'ToolTip': self.ToolTip}
class CreateInvoluteGear(BaseCommand):
NAME = "InvoluteGear"
GEAR_FUNCTION = involute_gear
Pixmap = os.path.join(BaseCommand.ICONDIR, 'involutegear.svg')
MenuText = 'involute gear'
ToolTip = 'involute gear'
class CreateInvoluteRack(BaseCommand):
NAME = "InvoluteRack"
GEAR_FUNCTION = involute_gear_rack
Pixmap = os.path.join(BaseCommand.ICONDIR, 'involuterack.svg')
MenuText = 'involute rack'
ToolTip = 'involute rack'
class CreateCrownGear(BaseCommand):
NAME = "CrownGear"
GEAR_FUNCTION = crown_gear
Pixmap = os.path.join(BaseCommand.ICONDIR, 'crowngear.svg')
MenuText = 'crown gear'
ToolTip = 'crown gear'
class CreateCycloideGear(BaseCommand):
NAME = "CycloidGear"
GEAR_FUNCTION = cycloide_gear
Pixmap = os.path.join(BaseCommand.ICONDIR, 'cycloidegear.svg')
MenuText = 'cycloide gear'
ToolTip = 'cycloide gear'
class CreateBevelGear(BaseCommand):
NAME = "BevelGear"
GEAR_FUNCTION = bevel_gear
Pixmap = os.path.join(BaseCommand.ICONDIR, 'bevelgear.svg')
MenuText = 'bevel gear'
ToolTip = 'bevel gear'

View File

@@ -1,611 +0,0 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
from __future__ import division
import os
import numpy as np
from pygears.involute_tooth import involute_tooth, involute_rack
from pygears.cycloide_tooth import cycloide_tooth
from pygears.bevel_tooth import bevel_tooth
from pygears._functions import rotation3D, rotation
import FreeCAD as App
import Part
from Part import BSplineCurve, Shape, Wire, Face, makePolygon, \
BRepOffsetAPI, Shell, makeLoft, Solid, Line, BSplineSurface, makeCompound,\
show, makePolygon, makeHelix, makeShell, makeSolid
__all__=["involute_gear",
"cycloide_gear",
"bevel_gear",
"involute_gear_rack",
"ViewProviderGear"]
def fcvec(x):
if len(x) == 2:
return(App.Vector(x[0], x[1], 0))
else:
return(App.Vector(x[0], x[1], x[2]))
class ViewProviderGear(object):
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def attach(self, vobj):
self.vobj = vobj
def getIcon(self):
__dirname__ = os.path.dirname(__file__)
return(os.path.join(__dirname__, "icons", "involutegear.svg"))
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class involute_gear(object):
"""FreeCAD gear"""
def __init__(self, obj):
self.involute_tooth = involute_tooth()
obj.addProperty(
"App::PropertyBool", "simple", "gear_parameter", "simple")
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyBool", "undercut", "gear_parameter", "undercut")
obj.addProperty(
"App::PropertyFloat", "shift", "gear_parameter", "shift")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyAngle", "pressure_angle", "involute_parameter", "pressure angle")
obj.addProperty(
"App::PropertyFloat", "clearance", "gear_parameter", "clearance")
obj.addProperty("App::PropertyInteger", "numpoints",
"gear_parameter", "number of points for spline")
obj.addProperty(
"App::PropertyAngle", "beta", "gear_parameter", "beta ")
obj.addProperty(
"App::PropertyBool", "double_helix", "gear_parameter", "double helix")
obj.addProperty(
"App::PropertyLength", "backlash", "tolerance", "backlash")
obj.addProperty(
"App::PropertyBool", "reversed_backlash", "tolerance", "backlash direction")
obj.addProperty(
"App::PropertyFloat", "head", "gear_parameter", "head_value * modul_value = additional length of head")
obj.addProperty("App::PropertyPythonObject", "gear", "gear_parameter", "test")
obj.addProperty("App::PropertyFloat", "dw", "computed", "pitch diameter", 1)
obj.gear = self.involute_tooth
obj.simple = False
obj.undercut = False
obj.teeth = 15
obj.module = '1. mm'
obj.shift = 0.
obj.pressure_angle = '20. deg'
obj.beta = '0. deg'
obj.height = '5. mm'
obj.clearance = 0.25
obj.head = 0.
obj.numpoints = 6
obj.double_helix = False
obj.backlash = '0.00 mm'
obj.reversed_backlash = False
self.obj = obj
obj.Proxy = self
def execute(self, fp):
fp.gear.double_helix = fp.double_helix
fp.gear.m_n = fp.module.Value
fp.gear.z = fp.teeth
fp.gear.undercut = fp.undercut
fp.gear.shift = fp.shift
fp.gear.pressure_angle = fp.pressure_angle.Value * np.pi / 180.
fp.gear.beta = fp.beta.Value * np.pi / 180
fp.gear.clearance = fp.clearance
fp.gear.backlash = fp.backlash.Value * (-fp.reversed_backlash + 0.5) * 2.
fp.gear.head = fp.head
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
rotated_pts = pts
rot = rotation(-fp.gear.phipart)
for i in range(fp.gear.z - 1):
rotated_pts = list(map(rot, rotated_pts))
pts.append(np.array([pts[-1][-1], rotated_pts[0][0]]))
pts += rotated_pts
pts.append(np.array([pts[-1][-1], pts[0][0]]))
if not fp.simple:
wi = []
for i in pts:
out = BSplineCurve()
out.interpolate(list(map(fcvec, i)))
wi.append(out.toShape())
wi = Wire(wi)
if fp.beta.Value == 0:
sh = Face(wi)
fp.Shape = sh.extrude(App.Vector(0, 0, fp.height.Value))
else:
fp.Shape = helicalextrusion(
wi, fp.height.Value, fp.height.Value * np.tan(fp.gear.beta) * 2 / fp.gear.d, fp.double_helix)
else:
rw = fp.gear.dw / 2
fp.Shape=Part.makeCylinder(rw,fp.height.Value)
fp.dw = fp.gear.dw
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class involute_gear_rack(object):
"""FreeCAD gear rack"""
def __init__(self, obj):
self.involute_rack = involute_rack()
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyLength", "thickness", "gear_parameter", "thickness")
obj.addProperty(
"App::PropertyAngle", "beta", "gear_parameter", "beta ")
obj.addProperty(
"App::PropertyAngle", "pressure_angle", "involute_parameter", "pressure angle")
obj.addProperty(
"App::PropertyBool", "double_helix", "gear_parameter", "double helix")
obj.addProperty(
"App::PropertyFloat", "head", "gear_parameter", "head_value * modul_value = additional length of head")
obj.addProperty("App::PropertyPythonObject", "rack", "test", "test")
obj.rack = self.involute_rack
obj.teeth = 15
obj.module = '1. mm'
obj.pressure_angle = '20. deg'
obj.height = '5. mm'
obj.thickness = '5 mm'
obj.beta = '0. deg'
self.obj = obj
obj.Proxy = self
def execute(self, fp):
fp.rack.m = fp.module.Value
fp.rack.z = fp.teeth
fp.rack.pressure_angle = fp.pressure_angle.Value * np.pi / 180.
fp.rack.thickness = fp.thickness.Value
fp.rack.beta = fp.beta.Value * np.pi / 180.
fp.rack.head = fp.head
fp.rack._update()
pts = fp.rack.points()
pol = Wire(makePolygon(list(map(fcvec, pts))))
if fp.beta.Value == 0:
face = Face(Wire(pol))
fp.Shape = face.extrude(fcvec([0., 0., fp.height.Value]))
elif fp.double_helix:
beta = fp.beta.Value * np.pi / 180.
pol2 = Part.Wire(pol)
pol2.translate(fcvec([0., np.tan(beta) * fp.height.Value / 2, fp.height.Value / 2]))
pol3 = Part.Wire(pol)
pol3.translate(fcvec([0., 0., fp.height.Value]))
fp.Shape = makeLoft([pol, pol2, pol3], True, True)
else:
beta = fp.beta.Value * np.pi / 180.
pol2 = Part.Wire(pol)
pol2.translate(fcvec([0., np.tan(beta) * fp.height.Value, fp.height.Value]))
fp.Shape = makeLoft([pol, pol2], True)
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class crown_gear(object):
def __init__(self, obj):
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty("App::PropertyInteger",
"other_teeth", "gear_parameter", "number of teeth of other gear")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyLength", "thickness", "gear_parameter", "thickness")
obj.addProperty(
"App::PropertyAngle", "pressure_angle", "involute_parameter", "pressure angle")
obj.addProperty("App::PropertyInteger",
"num_profiles", "accuracy", "number of profiles used for loft")
obj.addProperty("App::PropertyBool",
"construct", "accuracy", "number of profiles used for loft")
obj.teeth = 15
obj.other_teeth = 15
obj.module = '1. mm'
obj.pressure_angle = '20. deg'
obj.height = '2. mm'
obj.thickness = '5 mm'
obj.num_profiles = 4
obj.construct = True
self.obj = obj
obj.Proxy = self
def profile(self, m, r, r0, t_c, t_i, alpha_w, y0, y1, y2):
r_ew = m * t_i / 2
# 1: modifizierter Waelzkreisdurchmesser:
r_e = r / r0 * r_ew
# 2: modifizierter Schraegungswinkel:
alpha = np.arccos(r0 / r * np.cos(alpha_w))
# 3: winkel phi bei senkrechter stellung eines zahns:
phi = np.pi / t_i / 2 + (alpha - alpha_w) + (np.tan(alpha_w) - np.tan(alpha))
# 4: Position des Eingriffspunktes:
x_c = r_e * np.sin(phi)
dy = -r_e * np.cos(phi) + r_ew
# 5: oberer Punkt:
b = y1 - dy
a = np.tan(alpha) * b
x1 = a + x_c
# 6: unterer Punkt
d = y2 + dy
c = np.tan(alpha) * d
x2 = x_c - c
r *= np.cos(phi)
pts = [
[-x1, r, y0],
[-x2, r, y0 - y1 - y2],
[x2, r, y0 - y1 - y2],
[x1, r, y0]
]
pts.append(pts[0])
return pts
def execute(self, fp):
inner_diameter = fp.module.Value * fp.teeth
outer_diameter = inner_diameter + fp.height.Value * 2
inner_circle = Part.Wire(Part.makeCircle(inner_diameter / 2.))
outer_circle = Part.Wire(Part.makeCircle(outer_diameter / 2.))
inner_circle.reverse()
face = Part.Face([outer_circle, inner_circle])
solid = face.extrude(App.Vector([0., 0., -fp.thickness.Value]))
### cutting obj
alpha_w = np.deg2rad(fp.pressure_angle.Value)
m = fp.module.Value
t = fp.teeth
t_c = t
t_i = fp.other_teeth
rm = inner_diameter / 2
y0 = m * 0.5
y1 = m + y0
y2 = m
r0 = inner_diameter / 2 - fp.height.Value * 0.1
r1 = outer_diameter / 2 + fp.height.Value * 0.3
polies = []
for r_i in np.linspace(r0, r1, fp.num_profiles):
pts = self.profile(m, r_i, rm, t_c, t_i, alpha_w, y0, y1, y2)
poly = Wire(makePolygon(list(map(fcvec, pts))))
polies.append(poly)
loft = makeLoft(polies, True)
rot = App.Matrix()
rot.rotateZ(2 * np.pi / t)
if fp.construct:
cut_shapes = [solid]
for _ in range(t):
loft = loft.transformGeometry(rot)
cut_shapes.append(loft)
fp.Shape = Part.Compound(cut_shapes)
else:
for i in range(t):
loft = loft.transformGeometry(rot)
solid = solid.cut(loft)
fp.Shape = solid
def __getstate__(self):
pass
def __setstate__(self, state):
pass
class cycloide_gear(object):
"""FreeCAD gear"""
def __init__(self, obj):
self.cycloide_tooth = cycloide_tooth()
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyLength", "inner_diameter", "cycloid_parameter", "inner_diameter")
obj.addProperty(
"App::PropertyLength", "outer_diameter", "cycloid_parameter", "outer_diameter")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyBool", "double_helix", "gear_parameter", "double helix")
obj.addProperty(
"App::PropertyFloat", "clearance", "gear_parameter", "clearance")
obj.addProperty("App::PropertyInteger", "numpoints",
"gear_parameter", "number of points for spline")
obj.addProperty("App::PropertyAngle", "beta", "gear_parameter", "beta")
obj.addProperty(
"App::PropertyLength", "backlash", "gear_parameter", "backlash in mm")
obj.addProperty("App::PropertyPythonObject", "gear", "gear_parameter", "the python object")
obj.gear = self.cycloide_tooth
obj.teeth = 15
obj.module = '1. mm'
obj.inner_diameter = '5 mm'
obj.outer_diameter = '5 mm'
obj.beta = '0. deg'
obj.height = '5. mm'
obj.clearance = 0.25
obj.numpoints = 15
obj.backlash = '0.00 mm'
obj.double_helix = False
obj.Proxy = self
def execute(self, fp):
fp.gear.m = fp.module.Value
fp.gear.z = fp.teeth
fp.gear.z1 = fp.inner_diameter.Value
fp.gear.z2 = fp.outer_diameter.Value
fp.gear.clearance = fp.clearance
fp.gear.backlash = fp.backlash.Value
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
rotated_pts = pts
rot = rotation(-fp.gear.phipart)
for i in range(fp.gear.z - 1):
rotated_pts = list(map(rot, rotated_pts))
pts.append(np.array([pts[-1][-1], rotated_pts[0][0]]))
pts += rotated_pts
pts.append(np.array([pts[-1][-1], pts[0][0]]))
wi = []
for i in pts:
out = BSplineCurve()
out.interpolate(list(map(fcvec, i)))
wi.append(out.toShape())
wi = Wire(wi)
if fp.beta.Value == 0:
sh = Face(wi)
fp.Shape = sh.extrude(App.Vector(0, 0, fp.height.Value))
else:
fp.Shape = helicalextrusion(
wi, fp.height.Value, fp.height.Value * np.tan(fp.beta.Value * np.pi / 180) * 2 / fp.gear.d, fp.double_helix)
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class bevel_gear(object):
"""parameters:
pressure_angle: pressureangle, 10-30°
pitch_angle: cone angle, 0 < pitch_angle < pi/4
"""
def __init__(self, obj):
self.bevel_tooth = bevel_tooth()
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyAngle", "pitch_angle", "involute_parameter", "pitch_angle")
obj.addProperty(
"App::PropertyAngle", "pressure_angle", "involute_parameter", "pressure_angle")
obj.addProperty("App::PropertyLength", "m", "gear_parameter", "m")
obj.addProperty(
"App::PropertyFloat", "clearance", "gear_parameter", "clearance")
obj.addProperty("App::PropertyInteger", "numpoints",
"gear_parameter", "number of points for spline")
obj.addProperty("App::PropertyBool", "reset_origin", "gear_parameter",
"if value is true the gears outer face will match the z=0 plane")
obj.addProperty(
"App::PropertyLength", "backlash", "gear_parameter", "backlash in mm")
obj.addProperty("App::PropertyPythonObject", "gear", "gear_paramenter", "test")
obj.addProperty("App::PropertyAngle", "beta", "gear_paramenter", "test")
obj.gear = self.bevel_tooth
obj.m = '1. mm'
obj.teeth = 15
obj.pressure_angle = '20. deg'
obj.pitch_angle = '45. deg'
obj.height = '5. mm'
obj.numpoints = 6
obj.backlash = '0.00 mm'
obj.clearance = 0.1
obj.beta = '0 deg'
obj.reset_origin = True
self.obj = obj
obj.Proxy = self
def execute(self, fp):
fp.gear.z = fp.teeth
fp.gear.module = fp.m.Value
fp.gear.pressure_angle = (90 - fp.pressure_angle.Value) * np.pi / 180.
fp.gear.pitch_angle = fp.pitch_angle.Value * np.pi / 180
fp.gear.backlash = fp.backlash.Value
scale = fp.m.Value * fp.gear.z / 2 / np.tan(fp.pitch_angle.Value * np.pi / 180)
fp.gear.clearance = fp.clearance / scale
fp.gear._update()
pts = list(fp.gear.points(num=fp.numpoints))
rot = rotation3D(2 * np.pi / fp.teeth)
# if fp.beta.Value != 0:
# pts = [np.array([self.spherical_rot(j, fp.beta.Value * np.pi / 180.) for j in i]) for i in pts]
rotated_pts = pts
for i in range(fp.gear.z - 1):
rotated_pts = list(map(rot, rotated_pts))
pts.append(np.array([pts[-1][-1], rotated_pts[0][0]]))
pts += rotated_pts
pts.append(np.array([pts[-1][-1], pts[0][0]]))
wires = []
scale_0 = scale - fp.height.Value / 2
scale_1 = scale + fp.height.Value / 2
if fp.beta.Value == 0:
wires.append(makeBSplineWire([scale_0 * p for p in pts]))
wires.append(makeBSplineWire([scale_1 * p for p in pts]))
else:
for scale_i in np.linspace(scale_0, scale_1, 20):
# beta_i = (scale_i - scale_0) * fp.beta.Value * np.pi / 180
# rot = rotation3D(beta_i)
# points = [rot(pt) * scale_i for pt in pts]
angle = fp.beta.Value * np.pi / 180. * np.sin(np.pi / 4) / np.sin(fp.pitch_angle.Value * np.pi / 180.)
points = [np.array([self.spherical_rot(p, angle) for p in scale_i * pt]) for pt in pts]
wires.append(makeBSplineWire(points))
shape = makeLoft(wires, True)
if fp.reset_origin:
mat = App.Matrix()
mat.A33 = -1
mat.move(fcvec([0, 0, scale_1]))
shape = shape.transformGeometry(mat)
fp.Shape = shape
# fp.Shape = self.create_teeth(pts, pos1, fp.teeth)
def create_tooth(self):
w = []
scal1 = self.obj.m.Value * self.obj.gear.z / 2 / np.tan(
self.obj.pitch_angle.Value * np.pi / 180) - self.obj.height.Value / 2
scal2 = self.obj.m.Value * self.obj.gear.z / 2 / np.tan(
self.obj.pitch_angle.Value * np.pi / 180) + self.obj.height.Value / 2
s = [scal1, scal2]
pts = self.obj.gear.points(num=self.obj.numpoints)
for j, pos in enumerate(s):
w1 = []
scale = lambda x: fcvec(x * pos)
for i in pts:
i_scale = list(map(scale, i))
w1.append(i_scale)
w.append(w1)
surfs = []
w_t = zip(*w)
for i in w_t:
b = BSplineSurface()
b.interpolate(i)
surfs.append(b)
return Shape(surfs)
def spherical_rot(self, point, phi):
new_phi = np.sqrt(np.linalg.norm(point)) * phi
return rotation3D(new_phi)(point)
def create_teeth(self, pts, pos, teeth):
w1 = []
pts = [pt * pos for pt in pts]
rotated_pts = scaled_points
rot = rotation3D(- 2 * i * np.pi / teeth)
for i in range(teeth - 1):
rotated_pts = map(rot, rotated_pts)
pts.append(np.array([pts[-1][-1], rotated_pts[0][0]]))
pts += rotated_pts
s = Wire(Shape(w1).Edges)
wi = []
for i in range(teeth):
rot = App.Matrix()
rot.rotateZ(2 * i * np.pi / teeth)
tooth_rot = s.transformGeometry(rot)
if i != 0:
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = tooth_rot.Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
wi.append(tooth_rot)
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = wi[0].Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
return(Wire(wi))
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def helicalextrusion(wire, height, angle, double_helix = False):
direction = bool(angle < 0)
if double_helix:
first_spine = makeHelix(height * 2. * np.pi / abs(angle), 0.5 * height, 10., 0, direction)
first_solid = first_spine.makePipeShell([wire], True, True)
second_solid = first_solid.mirror(fcvec([0.,0.,0.]), fcvec([0,0,1]))
faces = first_solid.Faces + second_solid.Faces
faces = [f for f in faces if not on_mirror_plane(f, 0., fcvec([0., 0., 1.]))]
solid = makeSolid(makeShell(faces))
mat = App.Matrix()
mat.move(fcvec([0, 0, 0.5 * height]))
return solid.transformGeometry(mat)
else:
first_spine = makeHelix(height * 2 * np.pi / abs(angle), height, 10., 0, direction)
first_solid = first_spine.makePipeShell([wire], True, True)
return first_solid
def make_face(edge1, edge2):
v1, v2 = edge1.Vertexes
v3, v4 = edge2.Vertexes
e1 = Wire(edge1)
e2 = Line(v1.Point, v3.Point).toShape().Edges[0]
e3 = edge2
e4 = Line(v4.Point, v2.Point).toShape().Edges[0]
w = Wire([e3, e4, e1, e2])
return(Face(w))
def makeBSplineWire(pts):
wi = []
for i in pts:
out = BSplineCurve()
out.interpolate(list(map(fcvec, i)))
wi.append(out.toShape())
return Wire(wi)
def on_mirror_plane(face, z, direction, small_size=0.000001):
# the tolerance is very high. Maybe there is a bug in Part.makeHelix.
return (face.normalAt(0, 0).cross(direction).Length < small_size and
abs(face.CenterOfMass.z - z) < small_size)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,66 +0,0 @@
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import os
import FreeCADGui as Gui
import FreeCAD as App
__dirname__ = os.path.dirname(__file__)
try:
from FreeCADGui import Workbench
except ImportError as e:
App.Console.PrintWarning("you are using the GearWorkbench with an old version of FreeCAD (<0.16)")
App.Console.PrintWarning("the class Workbench is loaded, although not imported: magic")
class gearWorkbench(Workbench):
"""glider workbench"""
MenuText = "Gear"
ToolTip = "Gear Workbench"
Icon = os.path.join(__dirname__, 'icons', 'gearworkbench.svg')
commands = [
"CreateInvoluteGear",
"CreateInvoluteRack",
"CreateCycloideGear",
"CreateBevelGear",
"CreateCrownGear"]
def GetClassName(self):
return "Gui::PythonWorkbench"
def Initialize(self):
from .commands import CreateCycloideGear, CreateInvoluteGear
from .commands import CreateBevelGear, CreateInvoluteRack, CreateCrownGear
self.appendToolbar("Gear", self.commands)
self.appendMenu("Gear", self.commands)
Gui.addIconPath(App.getHomePath()+"Mod/gear/icons/")
Gui.addCommand('CreateInvoluteGear', CreateInvoluteGear())
Gui.addCommand('CreateCycloideGear', CreateCycloideGear())
Gui.addCommand('CreateBevelGear', CreateBevelGear())
Gui.addCommand('CreateInvoluteRack', CreateInvoluteRack())
Gui.addCommand('CreateCrownGear', CreateCrownGear())
def Activated(self):
pass
def Deactivated(self):
pass
Gui.addWorkbench(gearWorkbench())

7
freecad_gear/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
#!/usr/lib/python
from freecad_gear.gearfunc._involute_tooth import involute_rack, involute_tooth
from freecad_gear.gearfunc._cycloide_tooth import cycloide_tooth
from freecad_gear.gearfunc._bevel_tooth import bevel_tooth
import freecad_gear.freecad

View File

@@ -0,0 +1,143 @@
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import os
from PySide import QtGui, QtCore
freecad_found = True
try:
import FreeCADGui as Gui
import Part
import FreeCAD as App
except ImportError:
freecad_found = False
if freecad_found:
import freecad_gear as gear
from freecad_gear.freecad.commands import (createInvoluteGear,
createCycloidGear, createBevelGear, createInvoluteRack)
class gearToolBox(object):
def __init__(self):
mw = Gui.getMainWindow()
[
self.involuteGearAction,
self.involuteRackAction,
self.bevelGearAction,
self.cycloidGearAction,
self.dropdown_action] = [None, None, None, None, None]
self.defaultAction = createInvoluteGear
self.add_gear_wb()
mw.workbenchActivated.connect(self.add_gear_wb)
timer = mw.findChild(QtCore.QTimer, "activityTimer")
timer.connect(timer, QtCore.SIGNAL("timeout()"), self.checkDocument)
def add_gear_wb(self, *args):
print("Workbench_changed")
try:
wb = Gui.activeWorkbench()
except Exception as e:
return
if "PartWorkbench" in str(wb):
mainWindow = Gui.getMainWindow()
# add the module to Freecad
try:
if Gui.gear.gear_toolbar:
Gui.gear.gear_toolbar.show()
except:
pass
Gui.gear = gear.__class__("gear")
print(type(gear))
# create toolbar
Gui.gear.gear_toolbar = mainWindow.addToolBar("Part: GearToolbar")
Gui.gear.gear_toolbar.setObjectName("GearToolbar")
this_path = os.path.dirname(os.path.realpath(__file__))
self.dropdown = QtGui.QMenu("gear_menu", Gui.gear.gear_toolbar)
# create commands
icon = QtGui.QIcon(this_path + "/icons/involutegear.svg")
self.involuteGearAction = QtGui.QAction(icon, "involute gear", self.dropdown)
self.involuteGearAction.setObjectName("GearToolbar")
self.involuteGearAction.triggered.connect(
self.set_default_action(self.involuteGearAction, createInvoluteGear))
icon = QtGui.QIcon(this_path + "/icons/involuterack.svg")
self.involuteRackAction = QtGui.QAction(icon, "involute rack", self.dropdown)
self.involuteRackAction.setObjectName("GearToolbar")
self.involuteRackAction.triggered.connect(
self.set_default_action(self.involuteRackAction, createInvoluteRack))
icon = QtGui.QIcon(this_path + "/icons/cycloidegear.svg")
self.cycloidGearAction = QtGui.QAction(icon, "cycloid gear", self.dropdown)
self.cycloidGearAction.setObjectName("GearToolbar")
self.cycloidGearAction.triggered.connect(
self.set_default_action(self.cycloidGearAction, createCycloidGear))
icon = QtGui.QIcon(this_path + "/icons/bevelgear.svg")
self.bevelGearAction = QtGui.QAction(icon, "bevel gear", self.dropdown)
self.bevelGearAction.setObjectName("GearToolbar")
self.bevelGearAction.triggered.connect(
self.set_default_action(self.bevelGearAction, createBevelGear))
temp1 = self.dropdown.addAction(self.involuteGearAction)
temp2 = self.dropdown.addAction(self.involuteRackAction)
temp3 = self.dropdown.addAction(self.cycloidGearAction)
temp4 = self.dropdown.addAction(self.bevelGearAction)
self.dropdown.setIcon(self.involuteGearAction.icon())
temp5 = Gui.gear.gear_toolbar.addAction(self.dropdown.menuAction())
self.checkDocument()
self.defaultCommand = createInvoluteGear
self.dropdown.menuAction().triggered.connect(self.defaultCommand)
def set_default_action(self, action, command):
def cb(*args):
self.dropdown.setIcon(action.icon())
self.defaultCommand = command
command()
return cb
def checkDocument(self, *args):
enable = False
if App.ActiveDocument:
enable = True
for action in [self.involuteGearAction, self.involuteRackAction,
self.bevelGearAction, self.cycloidGearAction, self.dropdown.menuAction()]:
if action:
action.setEnabled(enable)
if freecad_found:
a = gearToolBox()

View File

@@ -0,0 +1,52 @@
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD as App
import FreeCADGui as Gui
from freecad_gear.gearfunc._Classes import involute_gear, cycloide_gear, bevel_gear, involute_gear_rack
def createInvoluteGear(*args):
a = App.ActiveDocument.addObject("Part::FeaturePython", "involute_gear")
involute_gear(a)
a.ViewObject.Proxy = 0.
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
def createInvoluteRack(*args):
a = App.ActiveDocument.addObject("Part::FeaturePython", "involute_gear")
involute_gear_rack(a)
a.ViewObject.Proxy = 0.
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
def createBevelGear(*args):
a = App.ActiveDocument.addObject("Part::FeaturePython", "bevel_gear")
bevel_gear(a)
a.ViewObject.Proxy = 0.
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
def createCycloidGear(*args):
a = App.ActiveDocument.addObject("Part::FeaturePython", "cycloide_gear")
cycloide_gear(a)
a.ViewObject.Proxy = 0.
App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,445 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
from __future__ import division
import FreeCAD as App
from _involute_tooth import involute_tooth, involute_rack
from _cycloide_tooth import cycloide_tooth
from _bevel_tooth import bevel_tooth
from Part import BSplineCurve, Shape, Wire, Face, makePolygon, \
BRepOffsetAPI, Shell, makeLoft, Solid, Line, BSplineSurface, Compound,\
show, makePolygon, makeLoft, makeHelix
import Part
from _functions import rotation3D
from numpy import pi, cos, sin, tan
import numpy
def fcvec(x):
if len(x) == 2:
return(App.Vector(x[0], x[1], 0))
else:
return(App.Vector(x[0], x[1], x[2]))
class involute_gear():
"""FreeCAD gear"""
def __init__(self, obj):
self.involute_tooth = involute_tooth()
obj.addProperty(
"App::PropertyBool", "simple", "gear_parameter", "simple")
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyBool", "undercut", "gear_parameter", "undercut")
obj.addProperty(
"App::PropertyFloat", "shift", "gear_parameter", "shift")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyAngle", "alpha", "involute_parameter", "alpha")
obj.addProperty(
"App::PropertyFloat", "clearence", "gear_parameter", "clearence")
obj.addProperty("App::PropertyInteger", "numpoints",
"gear_parameter", "number of points for spline")
obj.addProperty(
"App::PropertyAngle", "beta", "gear_parameter", "beta ")
obj.addProperty(
"App::PropertyLength", "backlash", "gear_parameter", "backlash in mm")
obj.addProperty("App::PropertyPythonObject", "gear", "test", "test")
obj.gear = self.involute_tooth
obj.simple = False
obj.undercut = False
obj.teeth = 15
obj.module = '1. mm'
obj.shift = 0.
obj.alpha = '20. deg'
obj.beta = '0. deg'
obj.height = '5. mm'
obj.clearence = 0.25
obj.numpoints = 6
obj.backlash = '0.00 mm'
self.obj = obj
obj.Proxy = self
def execute(self, fp):
fp.gear.m_n = fp.module.Value
fp.gear.z = fp.teeth
fp.gear.undercut = fp.undercut
fp.gear.shift = fp.shift
fp.gear.alpha = fp.alpha.Value * pi / 180.
fp.gear.beta = fp.beta.Value * pi / 180
fp.gear.clearence = fp.clearence
fp.gear.backlash = fp.backlash.Value
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
if not fp.simple:
wi = []
for i in pts:
out = BSplineCurve()
out.interpolate(map(fcvec, i))
wi.append(out)
s = Wire(Shape(wi).Edges)
wi = []
for i in range(fp.gear.z):
rot = App.Matrix()
rot.rotateZ(-i * fp.gear.phipart)
tooth_rot = s.transformGeometry(rot)
if i != 0:
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = tooth_rot.Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
wi.append(tooth_rot)
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = wi[0].Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
wi = Wire(wi)
fp.Shape = wi
if fp.beta.Value == 0:
sh = Face(wi)
fp.Shape = sh.extrude(App.Vector(0, 0, fp.height.Value))
else:
fp.Shape = helicalextrusion(
wi, fp.height.Value, fp.height.Value * tan(fp.gear.beta) * 2 / fp.gear.d)
else:
rw = fp.gear.dw / 2
circle = Part.Circle(App.Vector(0, 0, 0), App.Vector(0, 0, 1), rw)
wire = Part.Wire(circle.toShape())
face = Part.Face(wire)
fp.Shape = face.extrude(App.Vector(0, 0, fp.height.Value))
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class involute_gear_rack():
"""FreeCAD gear rack"""
def __init__(self, obj):
self.involute_rack = involute_rack()
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyLength", "thickness", "gear_parameter", "thickness")
obj.addProperty(
"App::PropertyAngle", "alpha", "involute_parameter", "alpha")
obj.addProperty("App::PropertyPythonObject", "rack", "test", "test")
obj.rack = self.involute_rack
obj.teeth = 15
obj.module = '1. mm'
obj.alpha = '20. deg'
obj.height = '5. mm'
obj.thickness = '5 mm'
self.obj = obj
obj.Proxy = self
def execute(self, fp):
fp.rack.m = fp.module.Value
fp.rack.z = fp.teeth
fp.rack.alpha = fp.alpha.Value * pi / 180.
fp.rack.thickness = fp.thickness.Value
fp.rack._update()
pts = fp.rack.points()
pol = Wire(makePolygon(map(fcvec, pts)))
fp.Shape = Face(Wire(pol)).extrude(fcvec([0., 0., fp.height]))
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class cycloide_gear():
"""FreeCAD gear"""
def __init__(self, obj):
self.cycloide_tooth = cycloide_tooth()
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "module", "gear_parameter", "module")
obj.addProperty(
"App::PropertyLength", "inner_diameter", "cycloid_parameter", "inner_diameter")
obj.addProperty(
"App::PropertyLength", "outer_diameter", "cycloid_parameter", "outer_diameter")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyFloat", "clearence", "gear_parameter", "clearence")
obj.addProperty("App::PropertyInteger", "numpoints",
"gear_parameter", "number of points for spline")
obj.addProperty("App::PropertyAngle", "beta", "gear_parameter", "beta")
obj.addProperty(
"App::PropertyLength", "backlash", "gear_parameter", "backlash in mm")
obj.addProperty("App::PropertyPythonObject", "gear", "test", "test")
obj.gear = self.cycloide_tooth
obj.teeth = 15
obj.module = '1. mm'
obj.inner_diameter = '5 mm'
obj.outer_diameter = '5 mm'
obj.beta = '0. deg'
obj.height = '5. mm'
obj.clearence = 0.25
obj.numpoints = 15
obj.backlash = '0.00 mm'
obj.Proxy = self
def execute(self, fp):
pass
fp.gear.m = fp.module.Value
fp.gear.z = fp.teeth
fp.gear.z1 = fp.inner_diameter.Value
fp.gear.z2 = fp.outer_diameter.Value
fp.gear.clearence = fp.clearence
fp.gear.backlash = fp.backlash.Value
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
wi = []
for i in pts:
out = BSplineCurve()
out.interpolate(map(fcvec, i))
wi.append(out)
s = Wire(Shape(wi).Edges)
wi = []
for i in range(fp.gear.z):
rot = App.Matrix()
rot.rotateZ(-i * fp.gear.phipart)
tooth_rot = s.transformGeometry(rot)
if i != 0:
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = tooth_rot.Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
wi.append(tooth_rot)
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = wi[0].Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
wi = Wire(wi)
if fp.beta.Value == 0:
sh = Face(wi)
fp.Shape = sh.extrude(App.Vector(0, 0, fp.height.Value))
else:
pass
fp.Shape = helicalextrusion(
wi, fp.height.Value, fp.height.Value * tan(fp.beta.Value * pi / 180) * 2 / fp.gear.d)
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class bevel_gear():
"""parameters:
alpha: pressureangle, 10-30°
gamma: cone angle, 0 < gamma < pi/4
"""
def __init__(self, obj):
self.bevel_tooth = bevel_tooth()
obj.addProperty("App::PropertyInteger",
"teeth", "gear_parameter", "number of teeth")
obj.addProperty(
"App::PropertyLength", "height", "gear_parameter", "height")
obj.addProperty(
"App::PropertyAngle", "gamma", "involute_parameter", "gamma")
obj.addProperty(
"App::PropertyAngle", "alpha", "involute_parameter", "alpha")
obj.addProperty("App::PropertyLength", "m", "gear_parameter", "m")
obj.addProperty(
"App::PropertyFloat", "clearence", "gear_parameter", "clearence")
obj.addProperty("App::PropertyInteger", "numpoints",
"gear_parameter", "number of points for spline")
obj.addProperty(
"App::PropertyLength", "backlash", "gear_parameter", "backlash in mm")
obj.addProperty("App::PropertyPythonObject", "gear", "test", "test")
obj.gear = self.bevel_tooth
obj.m = '1. mm'
obj.teeth = 15
obj.alpha = '70. deg'
obj.gamma = '45. deg'
obj.height = '5. mm'
obj.numpoints = 6
obj.backlash = '0.00 mm'
obj.clearence = 0.1
self.obj = obj
obj.Proxy = self
def execute1(self, fp):
fp.gear.z = fp.teeth
fp.gear.alpha = fp.alpha.Value * pi / 180.
fp.gear.gamma = fp.gamma.Value * pi / 180
fp.gear.backlash = fp.backlash
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
tooth = self.create_tooth()
teeth = [tooth]
rot = App.Matrix()
rot.rotateZ(2 * pi / fp.teeth)
top_cap = [i.Edges[0] for i in tooth.Faces]
bottom_cap = [i.Edges[3] for i in tooth.Faces]
for i in range(fp.teeth - 1):
new_tooth = teeth[-1].transformGeometry(rot)
edge1 = new_tooth.Faces[0].Edges[2]
edge2 = teeth[-1].Faces[-1].Edges[1]
face1 = make_face(edge1, edge2)
teeth.append(face1)
teeth.append(new_tooth)
top_cap.append(face1.Edges[3])
bottom_cap.append(face1.Edges[1])
top_cap += [i.Edges[0] for i in new_tooth.Faces]
bottom_cap += [i.Edges[3] for i in new_tooth.Faces]
edge1 = teeth[0].Faces[0].Edges[2]
edge2 = teeth[-1].Faces[-1].Edges[1]
face1 = make_face(edge1, edge2)
teeth.append(face1)
top_cap.append(face1.Edges[3])
bottom_cap.append(face1.Edges[1])
top_cap = Face(Wire(top_cap))
bottom_cap = Face(Wire(bottom_cap))
fcs = Compound(teeth).Faces
top_cap.reverse()
fp.Shape = Solid(Shell(fcs + [top_cap, bottom_cap]))
def execute(self, fp):
fp.gear.z = fp.teeth
fp.gear.module = fp.m.Value
fp.gear.alpha = fp.alpha.Value * pi / 180.
fp.gear.gamma = fp.gamma.Value * pi / 180
fp.gear.backlash = fp.backlash.Value
fp.gear.clearence = fp.clearence
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
scal1 = fp.m.Value * fp.gear.z / 2 / tan(
fp.gamma.Value * pi / 180) - fp.height.Value / 2
scal2 = fp.m.Value * fp.gear.z / 2 / tan(
fp.gamma.Value * pi / 180) + fp.height.Value / 2
fp.Shape = makeLoft([self.createteeths(pts, scal1, fp.teeth), self.createteeths(pts, scal2, fp.teeth)], True)
# fp.Shape = self.createteeths(pts, pos1, fp.teeth)
def create_tooth(self):
w = []
scal1 = self.obj.m.Value * self.obj.gear.z / 2 / tan(
self.obj.gamma.Value * pi / 180) - self.obj.height.Value / 2
scal2 = self.obj.m.Value * self.obj.gear.z / 2 / tan(
self.obj.gamma.Value * pi / 180) + self.obj.height.Value / 2
s = [scal1, scal2]
pts = self.obj.gear.points(num=self.obj.numpoints)
for j, pos in enumerate(s):
w1 = []
scale = lambda x: fcvec(x * pos)
for i in pts:
i_scale = map(scale, i)
w1.append(i_scale)
w.append(w1)
surfs = []
w_t = zip(*w)
for i in w_t:
b = BSplineSurface()
b.interpolate(i)
surfs.append(b)
return Shape(surfs)
def createteeths(self, pts, pos, teeth):
w1 = []
for i in pts:
scale = lambda x: x * pos
i_scale = map(scale, i)
out = BSplineCurve()
out.interpolate(map(fcvec, i_scale))
w1.append(out)
s = Wire(Shape(w1).Edges)
wi = []
for i in range(teeth):
rot = App.Matrix()
rot.rotateZ(2 * i * pi / teeth)
tooth_rot = s.transformGeometry(rot)
if i != 0:
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = tooth_rot.Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
wi.append(tooth_rot)
pt_0 = wi[-1].Edges[-1].Vertexes[0].Point
pt_1 = wi[0].Edges[0].Vertexes[-1].Point
wi.append(Wire([Line(pt_0, pt_1).toShape()]))
return(Wire(wi))
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def helicalextrusion(wire, height, angle):
face_a = Face(wire)
face_b = face_a.copy()
face_transform = App.Matrix()
face_transform.rotateZ(angle)
face_transform.move(App.Vector(0, 0, height))
face_b . transformShape(face_transform)
spine = Wire(Line(fcvec([0., 0, 0]), fcvec([0, 0, height])).toShape())
auxspine = makeHelix(height * 2 * pi / angle, height, 1.)
faces = [face_a, face_b]
pipeshell = BRepOffsetAPI.MakePipeShell(spine)
pipeshell.setSpineSupport(spine)
pipeshell.add(wire)
pipeshell.setAuxiliarySpine(auxspine, True, False)
assert(pipeshell.isReady())
pipeshell.build()
faces.extend(pipeshell.shape().Faces)
fullshell = Shell(faces)
solid = Solid(fullshell)
if solid.Volume < 0:
solid.reverse()
assert(solid.Volume >= 0)
return(solid)
def make_face(edge1, edge2):
v1, v2 = edge1.Vertexes
v3, v4 = edge2.Vertexes
e1 = Wire(edge1)
e2 = Line(v1.Point, v3.Point).toShape().Edges[0]
e3 = edge2
e4 = Line(v4.Point, v2.Point).toShape().Edges[0]
w = Wire([e3, e4, e1, e2])
return(Face(w))

View File

View File

@@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
from __future__ import division
from __future__ import division
from numpy import cos, sin, tan, arccos, arctan, pi, array, linspace, transpose, vstack, sqrt
from _functions import rotation3D, reflection3D
class bevel_tooth(object):
def __init__(self, alpha=70 * pi / 180, gamma=pi / 4, clearence=0.1,
z=21, backlash=0.00, module=0.25):
self.alpha = alpha
self.gamma = gamma
self.z = z
self.clearence = clearence
self.backlash = backlash
self.module = module
self.involute_end = arccos(
1 / sqrt(2) * sqrt((42. + 16.*cos(2.*self.alpha) +
6.*cos(4.*self.alpha) + cos(4.*self.alpha - 4.*self.gamma) - 8.*cos(2.*self.alpha - 2.*self.gamma) -
4.*cos(4.*self.alpha - 2.*self.gamma) + 24.*cos(2.*self.gamma) - 2.*cos(4.*self.gamma) -
8.*cos(2.*(self.alpha + self.gamma)) + cos(4.*(self.alpha + self.gamma)) -
4.*cos(4.*self.alpha + 2.*self.gamma) + 24.*cos((4.*sin(self.gamma))/self.z) +
4.*cos(2.*self.alpha - (4.*sin(self.gamma))/self.z) + 4.*cos(2.*self.alpha -
4.*self.gamma - (4.*sin(self.gamma))/self.z) - 8.*cos(2.*self.alpha - 2.*self.gamma -
(4.*sin(self.gamma))/self.z) + 24.*cos(4.*(self.gamma + sin(self.gamma)/self.z)) -
8.*cos(2.*(self.alpha + self.gamma + (2.*sin(self.gamma))/self.z)) + 4.*cos(2.*self.alpha +
(4.*sin(self.gamma))/self.z) + 16.*cos(2.*self.gamma + (4.*sin(self.gamma))/self.z) +
4.*cos(2.*self.alpha + 4.*self.gamma + (4.*sin(self.gamma))/self.z) + 32.*abs(cos(self.gamma +
(2.*sin(self.gamma))/self.z))*cos(self.alpha)*sqrt(4.*cos(2.*self.alpha) -
2.*(-2. + cos(2.*self.alpha - 2.*self.gamma) - 2.*cos(2.*self.gamma) + cos(2.*(self.alpha + self.gamma)) +
4.*cos(2.*self.gamma + (4.*sin(self.gamma))/self.z)))*sin(2.*self.gamma))/(-6. - 2.*cos(2.*self.alpha) +
cos(2.*self.alpha - 2.*self.gamma) - 2.*cos(2.*self.gamma) + cos(2.*(self.alpha + self.gamma)))**2))
self.involute_start = -pi/2. + \
arctan(1/tan(self.gamma)*1/cos(self.alpha))
self.involute_start_radius = self.getradius(self.involute_start)
self.r_f = sin(self.gamma - sin(gamma) * 2 / self.z) - self.clearence * sin(self.gamma)
self.z_f = cos(self.gamma - sin(gamma) * 2 / self.z)
self.add_foot = True
if self.involute_start_radius < self.r_f:
self.add_foot = False
self.involute_start = -arccos(
sqrt((42 + 16*cos(2*self.alpha) + 6*cos(4*self.alpha) -
4*cos(4*self.alpha - 2*self.gamma) - 8*cos(2*(self.alpha - self.gamma)) +
cos(4*(self.alpha - self.gamma)) + 24*cos(2*self.gamma) - 2*cos(4*self.gamma) -
8*cos(2*(self.alpha + self.gamma)) + cos(4*(self.alpha + self.gamma)) -
4*cos(2*(2*self.alpha + self.gamma)) + 24*cos((4*sin(self.gamma))/self.z) +
4*cos(2*self.alpha - (4*sin(self.gamma))/self.z) + 16*cos(2*self.gamma -
(4*sin(self.gamma))/self.z) + 24*cos(4*self.gamma - (4*sin(self.gamma))/self.z) +
4*cos(2*self.alpha + 4*self.gamma - (4*sin(self.gamma))/self.z) -
8*cos(2*(self.alpha + self.gamma - (2*sin(self.gamma))/self.z)) +
4*cos(2*self.alpha + (4*sin(self.gamma))/self.z) + 4*cos(2*self.alpha -
4*self.gamma + (4*sin(self.gamma))/self.z) - 8*cos(2*self.alpha - 2*self.gamma +
(4*sin(self.gamma))/self.z) + 32*sqrt(2)*sqrt(-(cos(self.alpha)**2*
(-2 - 2*cos(2*self.alpha) + cos(2*(self.alpha - self.gamma)) -
2*cos(2*self.gamma) + cos(2*(self.alpha + self.gamma)) +
4*cos(2*self.gamma - (4*sin(self.gamma))/self.z))*cos(self.gamma - (2*sin(self.gamma))/self.z)**2*
sin(2*self.gamma)**2)))/(-6 - 2*cos(2*self.alpha) + cos(2*(self.alpha - self.gamma)) -
2*cos(2*self.gamma) + cos(2*(self.alpha + self.gamma)))**2)/sqrt(2))
def involute_function_x(self):
def func(s):
return((
-(cos(s*1/sin(self.alpha)*1/sin(self.gamma))*sin(self.alpha)*sin(s)) +
(cos(s)*sin(self.gamma) + cos(self.alpha)*cos(self.gamma)*sin(s))*
sin(s*1/sin(self.alpha)*1/sin(self.gamma))))
return(func)
def involute_function_y(self):
def func(s):
return((
cos(s*1/sin(self.alpha)*1/sin(self.gamma))*(cos(s)*sin(self.gamma) +
cos(self.alpha)*cos(self.gamma)*sin(s)) + sin(self.alpha)*sin(s)*
sin(s*1/sin(self.alpha)*1/sin(self.gamma))))
return(func)
def involute_function_z(self):
def func(s):
return((
cos(self.gamma)*cos(s) - cos(self.alpha)*sin(self.gamma)*sin(s)))
return(func)
def getradius(self, s):
x = self.involute_function_x()
y = self.involute_function_y()
rx = x(s)
ry = y(s)
return(sqrt(rx**2 + ry**2))
def involute_points(self, num=10):
pts = linspace(self.involute_start, self.involute_end, num=num)
fx = self.involute_function_x()
x = array(map(fx, pts))
fy = self.involute_function_y()
y = array(map(fy, pts))
fz = self.involute_function_z()
z = array(map(fz, pts))
xyz = transpose(array([x, y,z]))
if self.add_foot:
p = xyz[0]
p1 =map(lambda x: x * (self.r_f / sqrt(p[0]**2 + p[1]**2)), p)
p1[2] = self.z_f
xyz=vstack([[p1], xyz])
xy = [[i[0]/i[2],i[1]/i[2],1.] for i in xyz]
backlash_rot = rotation3D(self.backlash / 4)
xy = backlash_rot(xy)
return(xy)
def points(self, num=10):
pts = self.involute_points(num = num)
rot = rotation3D(-pi/self.z/2)
pts = rot(pts)
ref = reflection3D(pi/2)
pts1 = ref(pts)[::-1]
rot = rotation3D(2*pi/self.z)
pt3 = rot(pts[0])
if self.add_foot:
return(array([
[pts[0],pts[1]],
pts[1:],
[pts[-1], pts1[0]],
pts1[:-1],
[pts1[-2], pts1[-1]]
]))
return(array([pts,[pts[-1],pts1[0]], pts1]))
else:
return(array([pts,[pts[-1],pts1[0]], pts1]))
def _update(self):
self.__init__(z = self.z, clearence = self.clearence,
alpha = self.alpha, gamma = self.gamma, backlash = self.backlash, module = self.module)
if __name__ == "__main__":
from matplotlib import pyplot
gear = bevel_tooth()
x = []
y = []
for i in gear.points(30):
for j in i:
x.append(j[0])
y.append(j[1])
pyplot.plot(x,y)
pyplot.show()

View File

@@ -19,27 +19,28 @@
#* * #* *
#*************************************************************************** #***************************************************************************
from __future__ import division
from __future__ import division from __future__ import division
from numpy import cos, sin, arccos, pi, array, linspace, transpose, vstack from numpy import cos, sin, arccos, pi, array, linspace, transpose, vstack
from ._functions import rotation, reflection from _functions import rotation, reflection
class cycloide_tooth(): class cycloide_tooth():
def __init__(self, z1 = 5, z2 = 5, z = 14, m = 5, clearance = 0.12, backlash = 0.00): def __init__(self, z1 = 5, z2 = 5, z = 14, m = 5, clearence = 0.12, backlash = 0.00):
self.m = m self.m = m
self.z = z self.z = z
self.clearance = clearance self.clearence = clearence
self.backlash = backlash self.backlash = backlash
self.z1 = z1 self.z1 = z1
self.z2 = z2 self.z2 = z2
self._calc_gear_factors() self._calc_gear_factors()
def _calc_gear_factors(self): def _calc_gear_factors(self):
self.d1 = self.z1 * self.m self.d1 = self.z1 * self.m
self.d2 = self.z2 * self.m self.d2 = self.z2 * self.m
self.phi = self.m * pi self.phi = self.m * pi
self.d = self.z * self.m self.d = self.z * self.m
self.da = self.d + 2*self.m self.da = self.d + 2*self.m
self.di = self.d - 2*self.m - self.clearance * self.m self.di = self.d - 2*self.m - self.clearence * self.m
self.phipart = 2 * pi / self.z self.phipart = 2 * pi / self.z
def epicycloide_x(self): def epicycloide_x(self):
@@ -86,10 +87,10 @@ class cycloide_tooth():
t_outer_end = self.outer_end() t_outer_end = self.outer_end()
t_vals_outer = linspace(0, t_outer_end, num) t_vals_outer = linspace(0, t_outer_end, num)
t_vals_inner = linspace(t_inner_end,0,num) t_vals_inner = linspace(t_inner_end,0,num)
pts_outer_x = list(map(outer_x, t_vals_outer)) pts_outer_x = map(outer_x, t_vals_outer)
pts_outer_y = list(map(outer_y, t_vals_outer)) pts_outer_y = map(outer_y, t_vals_outer)
pts_inner_x = list(map(inner_x, t_vals_inner)) pts_inner_x = map(inner_x, t_vals_inner)
pts_inner_y = list(map(inner_y, t_vals_inner)) pts_inner_y = map(inner_y, t_vals_inner)
pts_outer = transpose([pts_outer_x, pts_outer_y]) pts_outer = transpose([pts_outer_x, pts_outer_y])
pts_inner = transpose([pts_inner_x, pts_inner_y]) pts_inner = transpose([pts_inner_x, pts_inner_y])
pts1 = vstack([pts_inner[:-2],pts_outer]) pts1 = vstack([pts_inner[:-2],pts_outer])
@@ -102,7 +103,7 @@ class cycloide_tooth():
def _update(self): def _update(self):
self.__init__(m = self.m, z = self.z, z1 = self.z1, z2 = self.z2, self.__init__(m = self.m, z = self.z, z1 = self.z1, z2 = self.z2,
clearance = self.clearance, backlash = self.backlash) clearence = self.clearence, backlash = self.backlash)
if __name__ == "__main__": if __name__ == "__main__":
from matplotlib import pyplot from matplotlib import pyplot

View File

@@ -22,30 +22,28 @@
from __future__ import division from __future__ import division
from numpy import sin, cos, dot, array, ndarray, vstack, transpose, sqrt from numpy import sin, cos, dot, array, ndarray, vstack, transpose, sqrt
from numpy.linalg import solve from numpy.linalg import solve
import numpy as np
def reflection(pressure_angle): def reflection(alpha):
mat = array( mat = array(
[[cos(2 * pressure_angle), -sin(2 * pressure_angle)], [-sin(2 * pressure_angle), -cos(2 * pressure_angle)]]) [[cos(2 * alpha), -sin(2 * alpha)], [-sin(2 * alpha), -cos(2 * alpha)]])
def func(x): def func(x):
return(dot(x, mat)) return(dot(x, mat))
return(func) return(func)
def reflection3D(pressure_angle): def reflection3D(alpha):
mat = array([[cos(2 * pressure_angle), -sin(2 * pressure_angle), 0.], mat = array([[cos(2 * alpha), -sin(2 * alpha), 0.],
[-sin(2 * pressure_angle), -cos(2 * pressure_angle), 0.], [0., 0., 1.]]) [-sin(2 * alpha), -cos(2 * alpha), 0.], [0., 0., 1.]])
def func(x): def func(x):
return(dot(x, mat)) return(dot(x, mat))
return(func) return(func)
def rotation(pressure_angle, midpoint=None): def rotation(alpha, midpoint=[0, 0]):
midpoint = midpoint or [0., 0.] mat = array([[cos(alpha), -sin(alpha)], [sin(alpha), cos(alpha)]])
mat = array([[cos(pressure_angle), -sin(pressure_angle)], [sin(pressure_angle), cos(pressure_angle)]])
midpoint = array(midpoint) midpoint = array(midpoint)
vec = midpoint - dot(midpoint, mat) vec = midpoint - dot(midpoint, mat)
trans = translation(vec) trans = translation(vec)
@@ -55,11 +53,11 @@ def rotation(pressure_angle, midpoint=None):
return(func) return(func)
def rotation3D(pressure_angle): def rotation3D(alpha):
mat = array( mat = array(
[ [
[cos(pressure_angle), -sin(pressure_angle), 0.], [cos(alpha), -sin(alpha), 0.],
[sin(pressure_angle), cos(pressure_angle), 0.], [sin(alpha), cos(alpha), 0.],
[0., 0., 1.]]) [0., 0., 1.]])
def func(xx): def func(xx):
@@ -72,7 +70,7 @@ def translation(vec):
return([x[0] + vec[0], x[1] + vec[1]]) return([x[0] + vec[0], x[1] + vec[1]])
def func(x): def func(x):
return(array(list(map(trans, x)))) return(array(map(trans, x)))
return(func) return(func)
@@ -100,8 +98,8 @@ def trim(p1, p2, p3, p4):
return(p2) return(p2)
try: try:
g, h = solve(transpose([-a2 + a1, a4 - a3]), a1 - a3) g, h = solve(transpose([-a2 + a1, a4 - a3]), a1 - a3)
except Exception as e: except:
print(e) print(Exception)
return(False) return(False)
else: else:
if 0. < g < 1. and 0. < h < 1.: if 0. < g < 1. and 0. < h < 1.:
@@ -138,7 +136,10 @@ def trimfunc(l1, l2):
def norm(vec1, vec2): def norm(vec1, vec2):
vec = array(vec2) - array(vec1) vec = array(vec2) - array(vec1)
return np.linalg.norm(vec) out = 0
for i in vec:
out += i ** 2
return(sqrt(out))
def nearestpts(evolv, underc): def nearestpts(evolv, underc):
@@ -159,15 +160,3 @@ def nearestpts(evolv, underc):
jk += 1 jk += 1
ik += 1 ik += 1
return([vstack([underc[:jout], evolv[iout]]), evolv[iout:]]) return([vstack([underc[:jout], evolv[iout]]), evolv[iout:]])
def intersection_line_circle(p1, p2, r):
"""return the intersection point of a line from p1 to p2 and a sphere of radius 1 and
midpoint 0,0,0"""
d = p2 - p1
d /= np.linalg.norm(d)
p_half = d.dot(p1)
q = p1.dot(p1) - r ** 2
t = -p_half + sqrt(p_half ** 2 - q)
return p1 + d * t

View File

@@ -21,46 +21,44 @@
from __future__ import division from __future__ import division
from numpy import tan, cos, sin, sqrt, arctan, pi, array, linspace, transpose, vstack, ndarray from numpy import tan, cos, sin, sqrt, arctan, pi, array, linspace, transpose, vstack, ndarray
from ._functions import nearestpts, rotation, reflection, trimfunc, norm, translation from _functions import nearestpts, rotation, reflection, trimfunc, norm, translation
import numpy as np import numpy as np
class involute_tooth(): class involute_tooth():
def __init__(self, m=5, z=15, pressure_angle=20 * pi / 180., clearance=0.12, shift=0.5, beta=0., def __init__(self, m=5, z=15, alpha=20 * pi / 180., clearence=0.12, shift=0.5, beta=0., undercut=False, backlash=0.00):
undercut=False, backlash=0.00, head=0.00): self.alpha = alpha
self.pressure_angle = pressure_angle
self.beta = beta self.beta = beta
self.m_n = m self.m_n = m
self.z = z self.z = z
self.undercut = undercut self.undercut = undercut
self.shift = shift self.shift = shift
self.clearance = clearance self.clearence = clearence
self.backlash = backlash self.backlash = backlash
self.head = head # factor, rename!!!
self._calc_gear_factors() self._calc_gear_factors()
def _calc_gear_factors(self): def _calc_gear_factors(self):
self.pressure_angle_t = arctan(tan(self.pressure_angle) / cos(self.beta)) self.alpha_t = arctan(tan(self.alpha) / cos(self.beta))
self.m = self.m_n / cos(self.beta) self.m = self.m_n / cos(self.beta)
self.c = self.clearance * self.m_n self.c = self.clearence * self.m_n
self.midpoint = [0., 0.] self.midpoint = [0., 0.]
self.d = self.z * self.m self.d = self.z * self.m
self.dw = self.m * self.z self.dw = self.m * self.z
self.da = self.dw + 2. * self.m_n + 2. * (self.shift + self.head) * self.m_n self.da = self.dw + 2. * self.m_n + 2. * self.shift * self.m_n
self.df = self.dw - 2. * self.m_n - \ self.df = self.dw - 2. * self.m_n - \
2 * self.c + 2. * self.shift * self.m_n 2 * self.c + 2. * self.shift * self.m_n
self.dg = self.d * cos(self.pressure_angle_t) self.dg = self.d * cos(self.alpha_t)
self.phipart = 2 * pi / self.z self.phipart = 2 * pi / self.z
self.undercut_end = sqrt(-self.df ** 2 + self.da ** 2) / self.da self.undercut_end = sqrt(-self.df ** 2 + self.da ** 2) / self.da
self.undercut_rot = (-self.df / self.dw * tan(arctan((2 * ((self.m * pi) / 4. - self.undercut_rot = (-self.df / self.dw * tan(arctan((2 * ((self.m * pi) / 4. -
(self.c + self.m_n) * tan(self.pressure_angle_t))) / self.df))) (self.c + self.m_n) * tan(self.alpha_t))) / self.df)))
self.involute_end = sqrt(self.da ** 2 - self.dg ** 2) / self.dg self.involute_end = sqrt(self.da ** 2 - self.dg ** 2) / self.dg
self.involute_rot1 = sqrt(-self.dg ** 2 + (self.dw) ** 2) / self.dg - arctan( self.involute_rot1 = sqrt(-self.dg ** 2 + (self.dw) ** 2) / self.dg - arctan(
sqrt(-self.dg ** 2 + (self.dw) ** 2) / self.dg) sqrt(-self.dg ** 2 + (self.dw) ** 2) / self.dg)
self.involute_rot2 = self.m / \ self.involute_rot2 = self.m / \
(self.d) * (pi / 2 + 2 * self.shift * tan(self.pressure_angle_t)) (self.d) * (pi / 2 + 2 * self.shift * tan(self.alpha_t))
self.involute_rot2 = 1 / self.z * (pi / 2 + 2 * self.shift * tan(self.pressure_angle_t)) self.involute_rot2 = 1 / self.z * (pi / 2 + 2 * self.shift * tan(self.alpha_t))
self.involute_rot = self.involute_rot1 + self.involute_rot2 self.involute_rot = self.involute_rot1 + self.involute_rot2
self.involute_start = 0. self.involute_start = 0.
if self.dg <= self.df: if self.dg <= self.df:
@@ -70,9 +68,9 @@ class involute_tooth():
def undercut_points(self, num=10): def undercut_points(self, num=10):
pts = linspace(0, self.undercut_end, num=num) pts = linspace(0, self.undercut_end, num=num)
fx = self.undercut_function_x() fx = self.undercut_function_x()
x = array(list(map(fx, pts))) x = array(map(fx, pts))
fy = self.undercut_function_y() fy = self.undercut_function_y()
y = array(list(map(fy, pts))) y = array(map(fy, pts))
xy = transpose([x, y]) xy = transpose([x, y])
rotate = rotation( rotate = rotation(
self.undercut_rot + self.phipart / 2 - self.backlash / 4) self.undercut_rot + self.phipart / 2 - self.backlash / 4)
@@ -82,9 +80,9 @@ class involute_tooth():
def involute_points(self, num=10): def involute_points(self, num=10):
pts = linspace(self.involute_start, self.involute_end, num=num) pts = linspace(self.involute_start, self.involute_end, num=num)
fx = self.involute_function_x() fx = self.involute_function_x()
x = array(list(map(fx, pts))) x = array(map(fx, pts))
fy = self.involute_function_y() fy = self.involute_function_y()
y = array(list(map(fy, pts))) y = array(map(fy, pts))
rot = rotation(self.involute_rot - self.backlash / 4) rot = rotation(self.involute_rot - self.backlash / 4)
xy = rot(transpose(array([x, y]))) xy = rot(transpose(array([x, y])))
return(xy) return(xy)
@@ -147,44 +145,38 @@ class involute_tooth():
def _update(self): def _update(self):
self.__init__(m = self.m_n, z = self.z, self.__init__(m = self.m_n, z = self.z,
pressure_angle = self.pressure_angle, clearance = self.clearance, shift = self.shift, alpha = self.alpha, clearence = self.clearence, shift = self.shift,
beta = self.beta, undercut = self.undercut, backlash = self.backlash, head = self.head) beta = self.beta, undercut = self.undercut, backlash = self.backlash)
class involute_rack(object): class involute_rack(object):
def __init__(self, m=5, z=15, pressure_angle=20 * pi / 180., thickness=5, beta=0, head=0): def __init__(self, m=5, z=15, alpha=20 * pi / 180., thickness=5):
self.pressure_angle = pressure_angle self.alpha = alpha
self.thickness = thickness self.thickness = thickness
self.m = m self.m = m
self.z = z self.z = z
self.beta = beta
self.head = head
def _update(self): def _update(self):
self.__init__(m = self.m, z = self.z, pressure_angle = self.pressure_angle, self.__init__(m = self.m, z = self.z, alpha = self.alpha, thickness = self.thickness)
thickness=self.thickness, beta=self.beta, head=self.head)
def points(self, num=10): def points(self, num=10):
pressure_angle_t = arctan(tan(self.pressure_angle) / cos(self.beta)) a = 2 * self.m * tan(self.alpha)
m = self.m / cos(self.beta) b = ((self.m * pi) / 2 - a) / 2
a = (2 + self.head) * m * tan(pressure_angle_t)
b = (m * pi) / 4 - (1 + self.head) * m * tan(pressure_angle_t)
tooth= [ tooth= [
[-self.m, -a - b], [self.m, -a - b],
[self.m * (1 + self.head), -b], [-self.m, -b],
[self.m * (1 + self.head), b], [-self.m, b],
[-self.m, a + b] [self.m, a + b]
] ]
teeth = [tooth] teeth = [tooth]
trans = translation([0., m * pi, 0.]) trans = translation([0., self.m * pi, 0.])
for i in range(self.z - 1): for i in range(self.z):
teeth.append(trans(teeth[-1])) teeth.append(trans(teeth[-1]))
teeth = list(np.vstack(teeth)) teeth = list(np.vstack(teeth))
teeth.append(list(teeth[-1])) teeth.append(list(teeth[-1]))
teeth[-1][0] -= self.thickness teeth[-1][0] += self.thickness
teeth.append(list(teeth[0])) teeth.append(list(teeth[0]))
teeth[-1][0] -= self.thickness teeth[-1][0] += self.thickness
teeth.append(teeth[0]) teeth.append(teeth[0])
return(teeth) return(teeth)

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1 +0,0 @@
__version__ = "0.01"

View File

@@ -1,169 +0,0 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* *
#* 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. *
#* *
#* This program 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 this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
from __future__ import division
from __future__ import division
from numpy import cos, sin, tan, arccos, arctan, pi, array, linspace, transpose, vstack, sqrt
import numpy as np
from ._functions import rotation3D, reflection3D, intersection_line_circle
class bevel_tooth(object):
def __init__(self, pressure_angle=70 * pi / 180, pitch_angle=pi / 4, clearance=0.1,
z=21, backlash=0.00, module=0.25):
self.pressure_angle = pressure_angle
self.pitch_angle = pitch_angle
self.z = z
self.clearance = clearance
self.backlash = backlash
self.module = module
self.involute_end = arccos(
1 / sqrt(2) * sqrt((42. + 16.*cos(2.*self.pressure_angle) +
6.*cos(4.*self.pressure_angle) + cos(4.*self.pressure_angle - 4.*self.pitch_angle) - 8.*cos(2.*self.pressure_angle - 2.*self.pitch_angle) -
4.*cos(4.*self.pressure_angle - 2.*self.pitch_angle) + 24.*cos(2.*self.pitch_angle) - 2.*cos(4.*self.pitch_angle) -
8.*cos(2.*(self.pressure_angle + self.pitch_angle)) + cos(4.*(self.pressure_angle + self.pitch_angle)) -
4.*cos(4.*self.pressure_angle + 2.*self.pitch_angle) + 24.*cos((4.*sin(self.pitch_angle))/self.z) +
4.*cos(2.*self.pressure_angle - (4.*sin(self.pitch_angle))/self.z) + 4.*cos(2.*self.pressure_angle -
4.*self.pitch_angle - (4.*sin(self.pitch_angle))/self.z) - 8.*cos(2.*self.pressure_angle - 2.*self.pitch_angle -
(4.*sin(self.pitch_angle))/self.z) + 24.*cos(4.*(self.pitch_angle + sin(self.pitch_angle)/self.z)) -
8.*cos(2.*(self.pressure_angle + self.pitch_angle + (2.*sin(self.pitch_angle))/self.z)) + 4.*cos(2.*self.pressure_angle +
(4.*sin(self.pitch_angle))/self.z) + 16.*cos(2.*self.pitch_angle + (4.*sin(self.pitch_angle))/self.z) +
4.*cos(2.*self.pressure_angle + 4.*self.pitch_angle + (4.*sin(self.pitch_angle))/self.z) + 32.*abs(cos(self.pitch_angle +
(2.*sin(self.pitch_angle))/self.z))*cos(self.pressure_angle)*sqrt(4.*cos(2.*self.pressure_angle) -
2.*(-2. + cos(2.*self.pressure_angle - 2.*self.pitch_angle) - 2.*cos(2.*self.pitch_angle) + cos(2.*(self.pressure_angle + self.pitch_angle)) +
4.*cos(2.*self.pitch_angle + (4.*sin(self.pitch_angle))/self.z)))*sin(2.*self.pitch_angle))/(-6. - 2.*cos(2.*self.pressure_angle) +
cos(2.*self.pressure_angle - 2.*self.pitch_angle) - 2.*cos(2.*self.pitch_angle) + cos(2.*(self.pressure_angle + self.pitch_angle)))**2))
self.involute_start = -pi/2. + arctan(1/tan(self.pitch_angle)*1/cos(self.pressure_angle))
self.involute_start_radius = self.get_radius(self.involute_start)
self.r_f = sin(self.pitch_angle - sin(pitch_angle) * 2 / self.z) - self.clearance * sin(self.pitch_angle)
self.z_f = cos(self.pitch_angle - sin(pitch_angle) * 2 / self.z)
self.add_foot = True
# if self.involute_start_radius < self.r_f:
# self.add_foot = False
# self.involute_start = -arccos(
# sqrt((42 + 16*cos(2*self.pressure_angle) + 6*cos(4*self.pressure_angle) -
# 4*cos(4*self.pressure_angle - 2*self.pitch_angle) - 8*cos(2*(self.pressure_angle - self.pitch_angle)) +
# cos(4*(self.pressure_angle - self.pitch_angle)) + 24*cos(2*self.pitch_angle) - 2*cos(4*self.pitch_angle) -
# 8*cos(2*(self.pressure_angle + self.pitch_angle)) + cos(4*(self.pressure_angle + self.pitch_angle)) -
# 4*cos(2*(2*self.pressure_angle + self.pitch_angle)) + 24*cos((4*sin(self.pitch_angle))/self.z) +
# 4*cos(2*self.pressure_angle - (4*sin(self.pitch_angle))/self.z) + 16*cos(2*self.pitch_angle -
# (4*sin(self.pitch_angle))/self.z) + 24*cos(4*self.pitch_angle - (4*sin(self.pitch_angle))/self.z) +
# 4*cos(2*self.pressure_angle + 4*self.pitch_angle - (4*sin(self.pitch_angle))/self.z) -
# 8*cos(2*(self.pressure_angle + self.pitch_angle - (2*sin(self.pitch_angle))/self.z)) +
# 4*cos(2*self.pressure_angle + (4*sin(self.pitch_angle))/self.z) + 4*cos(2*self.pressure_angle -
# 4*self.pitch_angle + (4*sin(self.pitch_angle))/self.z) - 8*cos(2*self.pressure_angle - 2*self.pitch_angle +
# (4*sin(self.pitch_angle))/self.z) + 32*sqrt(2)*sqrt(-(cos(self.pressure_angle)**2*
# (-2 - 2*cos(2*self.pressure_angle) + cos(2*(self.pressure_angle - self.pitch_angle)) -
# 2*cos(2*self.pitch_angle) + cos(2*(self.pressure_angle + self.pitch_angle)) +
# 4*cos(2*self.pitch_angle - (4*sin(self.pitch_angle))/self.z))*cos(self.pitch_angle - (2*sin(self.pitch_angle))/self.z)**2*
# sin(2*self.pitch_angle)**2)))/(-6 - 2*cos(2*self.pressure_angle) + cos(2*(self.pressure_angle - self.pitch_angle)) -
# 2*cos(2*self.pitch_angle) + cos(2*(self.pressure_angle + self.pitch_angle)))**2)/sqrt(2))
def involute_function_x(self):
def func(s):
return((
-(cos(s*1/sin(self.pressure_angle)*1/sin(self.pitch_angle))*sin(self.pressure_angle)*sin(s)) +
(cos(s)*sin(self.pitch_angle) + cos(self.pressure_angle)*cos(self.pitch_angle)*sin(s))*
sin(s*1/sin(self.pressure_angle)*1/sin(self.pitch_angle))))
return(func)
def involute_function_y(self):
def func(s):
return((
cos(s*1/sin(self.pressure_angle)*1/sin(self.pitch_angle))*(cos(s)*sin(self.pitch_angle) +
cos(self.pressure_angle)*cos(self.pitch_angle)*sin(s)) + sin(self.pressure_angle)*sin(s)*
sin(s*1/sin(self.pressure_angle)*1/sin(self.pitch_angle))))
return(func)
def involute_function_z(self):
def func(s):
return((
cos(self.pitch_angle)*cos(s) - cos(self.pressure_angle)*sin(self.pitch_angle)*sin(s)))
return(func)
def get_radius(self, s):
x = self.involute_function_x()
y = self.involute_function_y()
rx = x(s)
ry = y(s)
return(sqrt(rx**2 + ry**2))
def involute_points(self, num=10):
pts = linspace(self.involute_start, self.involute_end, num=num)
fx = self.involute_function_x()
x = array(list(map(fx, pts)))
fy = self.involute_function_y()
y = array(list(map(fy, pts)))
fz = self.involute_function_z()
z = array(list(map(fz, pts)))
xyz = transpose(array([x, y, z]))
# conical projection to z=1
xy = [[i[0] / i[2], i[1] / i[2]] for i in xyz]
xy = array([[0, 0]] + xy)
r_cut = self.r_f / self.z_f
for i, point in enumerate(xy[1:]):
if point.dot(point) >= r_cut ** 2:
break
if i > 0:
self.add_foot = False
intersection_point = intersection_line_circle(xy[i], point, r_cut)
xy = array([intersection_point] + list(xy[i+1:]))
xyz = [[p[0], p[1], 1] for p in xy]
backlash_rot = rotation3D(self.backlash / 4)
xyz = backlash_rot(xyz)
return(xyz)
def points(self, num=10):
pts = self.involute_points(num=num)
rot = rotation3D(-pi/self.z/2)
pts = rot(pts)
ref = reflection3D(pi/2)
pts1 = ref(pts)[::-1]
rot = rotation3D(2*pi/self.z)
if self.add_foot:
return(array([
array([pts[0], pts[1]]),
pts[1:],
array([pts[-1], pts1[0]]),
pts1[:-1],
array([pts1[-2], pts1[-1]])
]))
else:
return(array([pts, array([pts[-1], pts1[0]]), pts1]))
def _update(self):
self.__init__(z=self.z, clearance=self.clearance,
pressure_angle=self.pressure_angle,
pitch_angle=self.pitch_angle,
backlash=self.backlash, module=self.module)
if __name__ == "__main__":
from matplotlib import pyplot
gear = bevel_tooth(z=60, clearance=0.0, pitch_angle=np.deg2rad(45))
x, y, z = gear.involute_points().T
pyplot.plot(x, y)
pyplot.show()

View File

@@ -1,23 +0,0 @@
import numpy as np
from scipy import optimize as opt
def computeShiftedGears(m, alpha, t1, t2, x1, x2):
"""Summary
Args:
m (float): common module of both gears [length]
alpha (float): pressure-angle [rad]
t1 (int): number of teeth of gear1
t2 (int): number of teeth of gear2
x1 (float): relative profile-shift of gear1
x2 (float): relative profile-shift of gear2
Returns:
(float, float): distance between gears [length], pressure angle of the assembly [rad]
"""
inv = lambda x: np.tan(x) - x
inv_alpha_w = inv(alpha) + 2 * np.tan(alpha) * (x1 + x2) / (t1 + t2)
root_inv = lambda x: inv(x) - inv_alpha_w
alpha_w = opt.fsolve(root_inv, 0.)
dist = m * (t1+ t2) / 2 * np.cos(alpha) / np.cos(alpha_w)
return dist, alpha_w

2
setup.cfg Normal file
View File

@@ -0,0 +1,2 @@
[metadata]
description-file = README.md

View File

@@ -1,14 +1,18 @@
from setuptools import setup import sys
from pygears import __version__ import os
setup(name='freecad.gears', from setuptools import setup, find_packages
version=str(__version__),
packages=['freecad', setup(
'freecad.gears', name = 'freecad_gear',
'pygears'], version = '0.3',
maintainer="looooo", packages = ["freecad_gear", "freecad_gear/freecad/", "freecad_gear/gearfunc/"],
maintainer_email="sppedflyer@gmail.com", include_package_data=True,
url="https://github.com/looooo/FCGear", description = 'Some gears for freecad',
description="gears for FreeCAD", author = 'Lorenz L',
install_requires=['numpy'], author_email = 'sppedflyer@gmail.com',
include_package_data=True) url = 'https://github.com/looooo/FCGear',
download_url = 'https://github.com/looooo/FCGear/tarball/0.3',
keywords = ['gear', 'freecad'],
classifiers = [],
)