From 9e14aac76bab7b0a0d75095cd83e153a53fdff90 Mon Sep 17 00:00:00 2001 From: looooo Date: Tue, 2 Jan 2024 00:02:42 +0100 Subject: [PATCH] add timing gear t-shape --- freecad/gears/commands.py | 13 +- freecad/gears/features.py | 1 - freecad/gears/icons/timinggear_t.svg | 404 +++++++++++++++++++++++++++ freecad/gears/init_gui.py | 17 +- freecad/gears/timing_gear_t.py | 113 ++++++++ pygears/_functions.py | 5 +- 6 files changed, 544 insertions(+), 9 deletions(-) create mode 100644 freecad/gears/icons/timinggear_t.svg create mode 100644 freecad/gears/timing_gear_t.py diff --git a/freecad/gears/commands.py b/freecad/gears/commands.py index 212d65d..592b798 100644 --- a/freecad/gears/commands.py +++ b/freecad/gears/commands.py @@ -19,14 +19,13 @@ import os import FreeCAD import FreeCADGui as Gui + from .features import ( ViewProviderGear, InvoluteGear, InternalInvoluteGear, InvoluteGearRack, CycloidGearRack, -) -from .features import ( CycloidGear, BevelGear, CrownGear, @@ -36,6 +35,8 @@ HypoCycloidGear, BaseGear, ) +from .timing_gear_t import TimingGearT + from .connector import GearConnector, ViewProviderGearConnector @@ -167,6 +168,14 @@ class CreateWormGear(BaseCommand): ToolTip = "Create a Worm gear" +class CreateTimingGearT(BaseCommand): + NAME = "TimingGearT" + GEAR_FUNCTION = TimingGearT + Pixmap = os.path.join(BaseCommand.ICONDIR, "timinggear_t.svg") + MenuText = "Timing Gear T-shape" + ToolTip = "Create a Timing gear T-shape" + + class CreateTimingGear(BaseCommand): NAME = "TimingGear" GEAR_FUNCTION = TimingGear diff --git a/freecad/gears/features.py b/freecad/gears/features.py index 300170f..19209f5 100644 --- a/freecad/gears/features.py +++ b/freecad/gears/features.py @@ -16,7 +16,6 @@ # * * # *************************************************************************** -from __future__ import division import os import sys diff --git a/freecad/gears/icons/timinggear_t.svg b/freecad/gears/icons/timinggear_t.svg new file mode 100644 index 0000000..b25e312 --- /dev/null +++ b/freecad/gears/icons/timinggear_t.svg @@ -0,0 +1,404 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + GT + + + + + diff --git a/freecad/gears/init_gui.py b/freecad/gears/init_gui.py index 08ce227..6f3a8d7 100644 --- a/freecad/gears/init_gui.py +++ b/freecad/gears/init_gui.py @@ -105,6 +105,7 @@ class GearWorkbench(Workbench): "CreateBevelGear", "CreateCrownGear", "CreateWormGear", + "CreateTimingGearT", "CreateTimingGear", "CreateLanternGear", "CreateHypoCycloidGear", @@ -119,15 +120,20 @@ def Initialize(self): CreateCycloidGear, CreateInvoluteGear, CreateInternalInvoluteGear, + CreateBevelGear, + CreateInvoluteRack, + CreateCrownGear, + CreateWormGear, + CreateTimingGearT, + CreateTimingGear, + CreateLanternGear, + CreateHypoCycloidGear, + CreateCycloidRack, + CreateGearConnector ) - from .commands import CreateBevelGear, CreateInvoluteRack, CreateCrownGear - from .commands import CreateWormGear, CreateTimingGear, CreateLanternGear - from .commands import CreateHypoCycloidGear, CreateCycloidRack - from .commands import CreateGearConnector self.appendToolbar("Gear", self.commands) self.appendMenu("Gear", self.commands) - # Gui.addIconPath(App.getHomePath()+"Mod/gear/icons/") Gui.addCommand("CreateInvoluteGear", CreateInvoluteGear()) Gui.addCommand("CreateInternalInvoluteGear", CreateInternalInvoluteGear()) Gui.addCommand("CreateCycloidGear", CreateCycloidGear()) @@ -136,6 +142,7 @@ def Initialize(self): Gui.addCommand("CreateInvoluteRack", CreateInvoluteRack()) Gui.addCommand("CreateCrownGear", CreateCrownGear()) Gui.addCommand("CreateWormGear", CreateWormGear()) + Gui.addCommand("CreateTimingGearT", CreateTimingGearT()) Gui.addCommand("CreateTimingGear", CreateTimingGear()) Gui.addCommand("CreateLanternGear", CreateLanternGear()) Gui.addCommand("CreateHypoCycloidGear", CreateHypoCycloidGear()) diff --git a/freecad/gears/timing_gear_t.py b/freecad/gears/timing_gear_t.py new file mode 100644 index 0000000..4eded90 --- /dev/null +++ b/freecad/gears/timing_gear_t.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# *************************************************************************** +# * * +# * This program is free software: you can redistribute it and/or modify * +# * it under the terms of the GNU General Public License as published by * +# * the Free Software Foundation, either version 3 of the License, or * +# * (at your option) any later version. * +# * * +# * 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 General Public License for more details. * +# * * +# * You should have received a copy of the GNU General Public License * +# * along with this program. If not, see . * +# * * +# *************************************************************************** + + +import numpy as np +import scipy as sp + +import FreeCAD as App +import Part + +from pygears._functions import ( + rotation, + reflection +) + +from .features import BaseGear, fcvec + + +class TimingGearT(BaseGear): + def __init__(self, obj): + print("hello gear") + obj.addProperty("App::PropertyLength", "pitch", "base", "pitch of gear") + obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") + obj.addProperty("App::PropertyLength", "tooth_height", "base", "radial height of tooth") + obj.addProperty("App::PropertyLength", "u", "base", "radial distance from tooth-head to pitch circle") + obj.addProperty("App::PropertyAngle", "alpha", "base", "angle of tooth flanks") + obj.addProperty("App::PropertyLength", "height", "base", "extrusion height") + obj.pitch = "5. mm" + obj.teeth = 15 + obj.tooth_height = "1.2 mm" + obj.u = "0.6 mm" + obj.alpha = "40. deg" + obj.height = "5 mm" + self.obj = obj + obj.Proxy = self + + def generate_gear_shape(self, fp): + print("generate gear shape") + pitch = fp.pitch.Value + teeth = fp.teeth + u = fp.u.Value + tooth_height = fp.tooth_height.Value + alpha = fp.alpha.Value / 180. * np.pi # we need radiant + height = fp.height.Value + + r_p = pitch * teeth / 2. / np.pi + gamma_0 = pitch / r_p + gamma_1 = gamma_0 / 4 + + p_A = np.array([ + np.cos(-gamma_1), + np.sin(-gamma_1) + ]) * (r_p - u - tooth_height / 2) + + def line(s): + p = p_A + np.array([ + np.cos(alpha / 2 - gamma_1), + np.sin(alpha / 2 - gamma_1) + ]) * s + return p + + def dist_p1(s): + return (np.linalg.norm(line(s)) - (r_p - u - tooth_height)) ** 2 + + def dist_p2(s): + return (np.linalg.norm(line(s)) - (r_p - u)) ** 2 + + s1 = sp.optimize.minimize(dist_p1, 0.).x + s2 = sp.optimize.minimize(dist_p2, 0.).x + + p_1 = line(s1) + p_2 = line(s2) + + mirror = reflection(0.) # reflect the points at the x-axis + p_3, p_4 = mirror(np.array([p_2, p_1])) + + rot = rotation(-gamma_0) # why is the rotation in wrong direction ??? + p_5 = rot(np.array([p_1]))[0] # the rotation expects a list of points + + l1 = Part.LineSegment(fcvec(p_1), fcvec(p_2)).toShape() + l2 = Part.LineSegment(fcvec(p_2), fcvec(p_3)).toShape() + l3 = Part.LineSegment(fcvec(p_3), fcvec(p_4)).toShape() + l4 = Part.LineSegment(fcvec(p_4), fcvec(p_5)).toShape() + w = Part.Wire([l1, l2, l3, l4]) + + # now using a FreeCAD Matrix (this will turn in the right direction) + rot = App.Matrix() + rot.rotateZ(gamma_0) + wires = [] + for i in range(teeth): + w = w.transformGeometry(rot) + wires.append(w.copy()) + contour = Part.Wire(wires) + if height == 0: + return contour + else: + face = Part.Face(Part.Wire(wires)) + return face.extrude(App.Vector(0., 0., height)) diff --git a/pygears/_functions.py b/pygears/_functions.py index cddd399..80fefc1 100644 --- a/pygears/_functions.py +++ b/pygears/_functions.py @@ -22,9 +22,11 @@ def reflection(angle): - mat = array([[cos(2 * angle), -sin(2 * angle)], [-sin(2 * angle), -cos(2 * angle)]]) + mat = array([[cos(2 * angle), -sin(2 * angle)], + [-sin(2 * angle), -cos(2 * angle)]]) def func(x): + # why not use mat @ x??? return dot(x, mat) return func @@ -40,6 +42,7 @@ def reflection3D(angle): ) def func(x): + # why not use mat @ x return dot(x, mat) return func