Initial addition of fcsprocket feature. This is a PartDesign tool that allows for the simple creation of ANSI standard roller chain sprockets.
This commit is contained in:
committed by
Yorik van Havre
parent
45aef7b028
commit
1eef7064f8
@@ -16,6 +16,8 @@ if(BUILD_GUI)
|
||||
TestPartDesignGui.py
|
||||
InvoluteGearFeature.py
|
||||
InvoluteGearFeature.ui
|
||||
SprocketFeature.py
|
||||
SprocketFeature.ui
|
||||
)
|
||||
endif(BUILD_GUI)
|
||||
|
||||
@@ -60,6 +62,13 @@ set(PartDesign_GearScripts
|
||||
fcgear/svggear.py
|
||||
)
|
||||
|
||||
set(PartDesign_SprocketScripts
|
||||
fcsprocket/__init__.py
|
||||
fcsprocket/fcsprocket.py
|
||||
fcsprocket/fcsprocketdialog.py
|
||||
fcsprocket/sprocket.py
|
||||
)
|
||||
|
||||
set(PartDesign_WizardShaft
|
||||
WizardShaft/__init__.py
|
||||
WizardShaft/WizardShaft.svg
|
||||
@@ -76,6 +85,7 @@ add_custom_target(PartDesignScripts ALL SOURCES
|
||||
${PartDesign_OtherScripts}
|
||||
${PartDesign_TestScripts}
|
||||
${PartDesign_GearScripts}
|
||||
${PartDesign_SprocketScripts}
|
||||
)
|
||||
|
||||
fc_target_copy_resource(PartDesignScripts
|
||||
@@ -85,6 +95,7 @@ fc_target_copy_resource(PartDesignScripts
|
||||
${PartDesign_OtherScripts}
|
||||
${PartDesign_TestScripts}
|
||||
${PartDesign_GearScripts}
|
||||
${PartDesign_SprocketScripts}
|
||||
)
|
||||
|
||||
INSTALL(
|
||||
@@ -113,7 +124,13 @@ INSTALL(
|
||||
${PartDesign_GearScripts}
|
||||
DESTINATION
|
||||
Mod/PartDesign/fcgear
|
||||
|
||||
)
|
||||
|
||||
INSTALL(
|
||||
FILES
|
||||
${PartDesign_SprocketScripts}
|
||||
DESTINATION
|
||||
Mod/PartDesign/fcsprocket
|
||||
)
|
||||
|
||||
if(BUILD_FEM)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<file>icons/PartDesign_Revolution.svg</file>
|
||||
<file>icons/PartDesign_Scaled.svg</file>
|
||||
<file>icons/PartDesign_ShapeBinder.svg</file>
|
||||
<file>icons/PartDesign_Sprocket.svg</file>
|
||||
<file>icons/PartDesign_SubShapeBinder.svg</file>
|
||||
<file>icons/PartDesign_Subtractive_Box.svg</file>
|
||||
<file>icons/PartDesign_Subtractive_Cone.svg</file>
|
||||
|
||||
492
src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Sprocket.svg
Normal file
492
src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Sprocket.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 89 KiB |
@@ -509,9 +509,10 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
<< "PartDesign_Boolean"
|
||||
<< "Separator"
|
||||
//<< "PartDesign_Hole"
|
||||
<< "PartDesign_InvoluteGear"
|
||||
<< "Separator"
|
||||
<< "PartDesign_Migrate";
|
||||
<< "PartDesign_Migrate"
|
||||
<< "PartDesign_Sprocket"
|
||||
<< "PartDesign_InvoluteGear";
|
||||
|
||||
|
||||
// For 0.13 a couple of python packages like numpy, matplotlib and others
|
||||
// are not deployed with the installer on Windows. Thus, the WizardShaft is
|
||||
|
||||
@@ -51,6 +51,7 @@ class PartDesignWorkbench ( Workbench ):
|
||||
import PartDesign
|
||||
try:
|
||||
from PartDesign import InvoluteGearFeature
|
||||
from PartDesign import SprocketFeature
|
||||
except ImportError:
|
||||
print("Involute gear module cannot be loaded")
|
||||
#try:
|
||||
|
||||
232
src/Mod/PartDesign/SprocketFeature.py
Normal file
232
src/Mod/PartDesign/SprocketFeature.py
Normal file
@@ -0,0 +1,232 @@
|
||||
#***************************************************************************
|
||||
#* Copyright (c) 2020 Adam Spontarelli <adam@vector-space.org> *
|
||||
#* *
|
||||
#* 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, Part
|
||||
from fcsprocket import fcsprocket
|
||||
from fcsprocket import sprocket
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from FreeCADGui import PySideUic as uic
|
||||
|
||||
__title__="PartDesign SprocketObject management"
|
||||
__author__ = "Adam Spontarelli"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
|
||||
def makeSprocket(name):
|
||||
'''makeSprocket(name): makes a Sprocket'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",name)
|
||||
_Sprocket(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderSprocket(obj.ViewObject)
|
||||
#FreeCAD.ActiveDocument.recompute()
|
||||
if FreeCAD.GuiUp:
|
||||
body=FreeCADGui.ActiveDocument.ActiveView.getActiveObject("pdbody")
|
||||
part=FreeCADGui.ActiveDocument.ActiveView.getActiveObject("part")
|
||||
if body:
|
||||
body.Group=body.Group+[obj]
|
||||
elif part:
|
||||
part.Group=part.Group+[obj]
|
||||
return obj
|
||||
|
||||
class _CommandSprocket:
|
||||
"the Fem Sprocket command definition"
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : 'PartDesign_Sprocket',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PartDesign_Sprocket","Sprocket..."),
|
||||
'Accel': "",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PartDesign_Sprocket","Creates or edit the sprocket definition.")}
|
||||
|
||||
def Activated(self):
|
||||
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Sprocket")
|
||||
FreeCADGui.addModule("SprocketFeature")
|
||||
FreeCADGui.doCommand("SprocketFeature.makeSprocket('Sprocket')")
|
||||
FreeCADGui.doCommand("Gui.activeDocument().setEdit(App.ActiveDocument.ActiveObject.Name,0)")
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class _Sprocket:
|
||||
"The Sprocket object"
|
||||
def __init__(self,obj):
|
||||
self.Type = "Sprocket"
|
||||
obj.addProperty("App::PropertyInteger","NumberOfTeeth","Sprocket","Number of gear teeth")
|
||||
obj.addProperty("App::PropertyLength","Pitch","Sprocket","Chain Pitch")
|
||||
obj.addProperty("App::PropertyLength","RollerDiameter","Sprocket","Roller Diameter")
|
||||
obj.addProperty("App::PropertyString","ANSISize","Sprocket","ANSI Size")
|
||||
|
||||
obj.NumberOfTeeth = 50
|
||||
obj.Pitch = "0.375 in"
|
||||
obj.RollerDiameter = "0.20 in"
|
||||
obj.ANSISize = "35"
|
||||
|
||||
obj.Proxy = self
|
||||
|
||||
|
||||
def execute(self,obj):
|
||||
w = fcsprocket.FCWireBuilder()
|
||||
sprocket.CreateSprocket(w, obj.Pitch.Value, obj.NumberOfTeeth, obj.RollerDiameter.Value)
|
||||
|
||||
sprocketw = Part.Wire([o.toShape() for o in w.wire])
|
||||
obj.Shape = sprocketw
|
||||
obj.positionBySupport();
|
||||
return
|
||||
|
||||
|
||||
class _ViewProviderSprocket:
|
||||
"A View Provider for the Sprocket object"
|
||||
|
||||
def __init__(self,vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/PartDesign_Sprocket.svg"
|
||||
|
||||
def attach(self, vobj):
|
||||
self.ViewObject = vobj
|
||||
self.Object = vobj.Object
|
||||
|
||||
|
||||
def setEdit(self,vobj,mode):
|
||||
taskd = _SprocketTaskPanel(self.Object,mode)
|
||||
taskd.obj = vobj.Object
|
||||
taskd.update()
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
return True
|
||||
|
||||
def unsetEdit(self,vobj,mode):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
|
||||
class _SprocketTaskPanel:
|
||||
'''The editmode TaskPanel for Sprocket objects'''
|
||||
def __init__(self,obj,mode):
|
||||
self.obj = obj
|
||||
|
||||
self.form=FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/PartDesign/SprocketFeature.ui")
|
||||
self.form.setWindowIcon(QtGui.QIcon(":/icons/PartDesign_Sprocket.svg"))
|
||||
|
||||
QtCore.QObject.connect(self.form.Quantity_Pitch, QtCore.SIGNAL("valueChanged(double)"), self.pitchChanged)
|
||||
QtCore.QObject.connect(self.form.Quantity_RollerDiameter, QtCore.SIGNAL("valueChanged(double)"), self.rollerDiameterChanged)
|
||||
QtCore.QObject.connect(self.form.spinBox_NumberOfTeeth, QtCore.SIGNAL("valueChanged(int)"), self.numTeethChanged)
|
||||
QtCore.QObject.connect(self.form.comboBox_ANSISize, QtCore.SIGNAL("currentTextChanged(const QString)"), self.ANSISizeChanged)
|
||||
|
||||
self.update()
|
||||
|
||||
if mode == 0: # fresh created
|
||||
self.obj.Proxy.execute(self.obj) # calculate once
|
||||
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
def transferTo(self):
|
||||
"Transfer from the dialog to the object"
|
||||
self.obj.NumberOfTeeth = self.form.spinBox_NumberOfTeeth.value()
|
||||
self.obj.Pitch = self.form.Quantity_Pitch.text()
|
||||
self.obj.RollerDiameter = self.form.Quantity_RollerDiameter.text()
|
||||
self.obj.ANSISize = self.form.comboBox_ANSISize.currentText()
|
||||
|
||||
def transferFrom(self):
|
||||
"Transfer from the object to the dialog"
|
||||
self.form.spinBox_NumberOfTeeth.setValue(self.obj.NumberOfTeeth)
|
||||
self.form.Quantity_Pitch.setText(self.obj.Pitch.UserString)
|
||||
self.form.Quantity_RollerDiameter.setText(self.obj.RollerDiameter.UserString)
|
||||
self.form.comboBox_ANSISize.setCurrentText(self.obj.ANSISize)
|
||||
|
||||
def pitchChanged(self, value):
|
||||
self.obj.Pitch = value
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
def ANSISizeChanged(self, size):
|
||||
"""
|
||||
ANSI B29.1-2011 standard roller chain sizes in USCS units (inches)
|
||||
{size: [Pitch, Roller Diameter]}
|
||||
"""
|
||||
ANSIRollerTable = {"25": [0.250, 0.130],
|
||||
"35": [0.375, 0.200],
|
||||
"41": [0.500, 0.306],
|
||||
"40": [0.500, 0.312],
|
||||
"50": [0.625, 0.400],
|
||||
"60": [0.750, 0.469],
|
||||
"80": [1.000, 0.625],
|
||||
"100":[1.250, 0.750],
|
||||
"120":[1.500, 0.875],
|
||||
"140":[1.750, 1.000],
|
||||
"160":[2.000, 1.125],
|
||||
"180":[2.250, 1.460],
|
||||
"200":[2.500, 1.562],
|
||||
"240":[3.000, 1.875]}
|
||||
|
||||
self.obj.Pitch = str(ANSIRollerTable[size][0]) + " in"
|
||||
self.obj.RollerDiameter = str(ANSIRollerTable[size][1]) + " in"
|
||||
self.form.Quantity_Pitch.setText(self.obj.Pitch.UserString)
|
||||
self.form.Quantity_RollerDiameter.setText(self.obj.RollerDiameter.UserString)
|
||||
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
def rollerDiameterChanged(self, value):
|
||||
self.obj.RollerDiameter = value
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
|
||||
def numTeethChanged(self, value):
|
||||
self.obj.NumberOfTeeth = value
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
def getStandardButtons(self):
|
||||
return int(QtGui.QDialogButtonBox.Ok) | int(QtGui.QDialogButtonBox.Cancel)| int(QtGui.QDialogButtonBox.Apply)
|
||||
|
||||
def clicked(self,button):
|
||||
if button == QtGui.QDialogButtonBox.Apply:
|
||||
self.transferTo()
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
|
||||
def update(self):
|
||||
'fills the widgets'
|
||||
self.transferFrom()
|
||||
|
||||
def accept(self):
|
||||
self.transferTo()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
|
||||
def reject(self):
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PartDesign_Sprocket',_CommandSprocket())
|
||||
232
src/Mod/PartDesign/SprocketFeature.ui
Normal file
232
src/Mod/PartDesign/SprocketFeature.ui
Normal file
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SprocketParameter</class>
|
||||
<widget class="QWidget" name="SprocketParameter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>195</width>
|
||||
<height>142</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Sprocket parameter</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Number of teeth:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="spinBox_NumberOfTeeth">
|
||||
<property name="minimum">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>ANSI Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBox_ANSISize">
|
||||
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>25</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>35</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>41</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>40</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>50</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>60</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>80</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>100</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>120</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>140</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>160</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>180</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>200</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>240</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="2" column="0">
|
||||
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Chain Pitch:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="2" column="1">
|
||||
|
||||
<widget class="Gui::InputField" name="Quantity_Pitch">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">in</string>
|
||||
</property>
|
||||
<property name="decimals" stdset="0">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="maximum" stdset="0">
|
||||
<double>2000.000000000000000</double>
|
||||
</property>
|
||||
<property name="minimum" stdset="0">
|
||||
<double>0.01</double>
|
||||
</property>
|
||||
|
||||
<property name="singleStep" stdset="0">
|
||||
<double>0.001</double>
|
||||
</property>
|
||||
<property name="value" stdset="0">
|
||||
<double>0.375</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="3" column="0">
|
||||
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Roller Diameter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::InputField" name="Quantity_RollerDiameter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">in</string>
|
||||
</property>
|
||||
<property name="decimals" stdset="0">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="maximum" stdset="0">
|
||||
<double>50.000000000000000</double>
|
||||
</property>
|
||||
<property name="minimum" stdset="0">
|
||||
<double>0.01</double>
|
||||
</property>
|
||||
|
||||
<property name="singleStep" stdset="0">
|
||||
<double>0.01</double>
|
||||
</property>
|
||||
<property name="value" stdset="0">
|
||||
<double>0.20</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::InputField</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>Gui/InputField.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
24
src/Mod/PartDesign/fcsprocket/README.md
Normal file
24
src/Mod/PartDesign/fcsprocket/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
================================================
|
||||
FCSprocket: a Sprocket Generator for FreeCAD
|
||||
================================================
|
||||
|
||||
This is a simple sprocket generation tool. Sprockets are used in combination
|
||||
with roller chain to transmit power. The tooth profiles are drawn according
|
||||
to ANSI standards from the methods outlined in:
|
||||
|
||||
Standard handbook of chains : chains for power transmission and material
|
||||
handling. Boca Raton: Taylor & Francis, 2006. Print.
|
||||
|
||||
AND
|
||||
|
||||
Oberg, Erik, et al. Machinery's handbook : a reference book for the
|
||||
mechanical engineer, designer, manufacturing engineer, draftsman,
|
||||
toolmaker, and machinist. New York: Industrial Press, 2016. Print.
|
||||
|
||||
|
||||
This code is based on the work of David Douard and his implementation of the
|
||||
gear generator (fcgear) found in FreeCAD.
|
||||
|
||||
|
||||
Copyright 2020 Adam Spontarelli <adam@vector-space.org>.
|
||||
Distributed under the LGPL licence.
|
||||
1
src/Mod/PartDesign/fcsprocket/__init__.py
Normal file
1
src/Mod/PartDesign/fcsprocket/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
105
src/Mod/PartDesign/fcsprocket/fcsprocket.py
Normal file
105
src/Mod/PartDesign/fcsprocket/fcsprocket.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# (c) 2020 Adam Spontarelli <adam@vector-space.org>
|
||||
#
|
||||
# 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, Part
|
||||
from FreeCAD import Base, Console
|
||||
from . import sprocket
|
||||
rotate = sprocket.rotate
|
||||
|
||||
def makeSprocket(P, N, Dr):
|
||||
if FreeCAD.ActiveDocument is None:
|
||||
FreeCAD.newDocument("Sprocket")
|
||||
doc = FreeCAD.ActiveDocument
|
||||
w = FCWireBuilder()
|
||||
sprocket.CreateSprocket(w, P, N, Dr)
|
||||
sprocketw = Part.Wire([o.toShape() for o in w.wire])
|
||||
sprocket = doc.addObject("Part::Feature", "Sprocket")
|
||||
sprocket.Shape = sprocketw
|
||||
return sprocket
|
||||
|
||||
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.LineSegment(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 through 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
|
||||
66
src/Mod/PartDesign/fcsprocket/fcsprocketdialog.py
Normal file
66
src/Mod/PartDesign/fcsprocket/fcsprocketdialog.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# (c) 2020 Adam Spontarelli <adam@vector-space.org>
|
||||
#
|
||||
# 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 PySide import QtGui as qt
|
||||
import fcsprocket
|
||||
import FreeCAD, FreeCADGui
|
||||
|
||||
class SprocketCreationFrame(qt.QFrame):
|
||||
def __init__(self, parent=None):
|
||||
super(SprocketCreationFrame, self).__init__(parent)
|
||||
self.P = qt.QSpinBox(value=0.375)
|
||||
self.N = qt.QDoubleSpinBox(value=45)
|
||||
self.Dr = qt.QDoubleSpinBox(value=0.20)
|
||||
|
||||
l = qt.QFormLayout(self)
|
||||
l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
|
||||
l.addRow('Number of teeth:', self.N)
|
||||
l.addRow('Chain Pitch (in):', self.P)
|
||||
l.addRow('Roller Diameter (in):', self.Dr)
|
||||
|
||||
|
||||
class SprocketDialog(qt.QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(SprocketDialog, self).__init__(parent)
|
||||
self.gc = SprocketCreationFrame()
|
||||
|
||||
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('Sprocket creation dialog')
|
||||
|
||||
def accept(self):
|
||||
if FreeCAD.ActiveDocument is None:
|
||||
FreeCAD.newDocument("Sprocket")
|
||||
|
||||
gear = fcgear.makeSprocket(self.gc.m.value(),
|
||||
self.gc.Z.value(),
|
||||
self.gc.angle.value(),
|
||||
not self.gc.split.currentIndex())
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
return super(SprocketDialog, self).accept()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = qt.QApplication([])
|
||||
w = SprocketDialog()
|
||||
w.show()
|
||||
a.exec_()
|
||||
144
src/Mod/PartDesign/fcsprocket/sprocket.py
Normal file
144
src/Mod/PartDesign/fcsprocket/sprocket.py
Normal file
@@ -0,0 +1,144 @@
|
||||
# (c) 2020 Adam Spontarelli <adam@vector-space.org>
|
||||
#
|
||||
# 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, tan, sqrt, radians, acos, atan, asin, degrees
|
||||
import math
|
||||
|
||||
import sys
|
||||
if sys.version_info.major >= 3:
|
||||
xrange = range
|
||||
|
||||
|
||||
def CreateSprocket(w, P, N, Dr):
|
||||
"""
|
||||
Create a sprocket
|
||||
|
||||
w is the wirebuilder object (in which the sprocket will be constructed)
|
||||
P is the chain pitch
|
||||
N is the number of teeth
|
||||
Dr is the roller diameter
|
||||
|
||||
Remaining variables can be found in Standard Handbook of Chains
|
||||
"""
|
||||
Ds = 1.005 * Dr + (0.003 * 25.4)
|
||||
R = Ds / 2
|
||||
alpha = 35 + 60/N
|
||||
beta = 18 - 56 / N
|
||||
M = 0.8 * Dr * cos(radians(35) + radians(60/N))
|
||||
T = 0.8 * Dr * sin(radians(35) + radians(60/N))
|
||||
E = 1.3025 * Dr + (0.0015 * 25.4)
|
||||
W = 1.4 * Dr * cos(radians(180/N))
|
||||
V = 1.4 * Dr * sin(radians(180/N))
|
||||
F = Dr * (0.8 * cos(radians(18) - radians(56)/N) + 1.4 *
|
||||
cos(radians(17) - radians(64) / N) - 1.3025) - (0.0015 * 25.4)
|
||||
PD = P / (sin(radians(180)/N))
|
||||
H = sqrt(F**2 - (1.4 * Dr - P/2)**2)
|
||||
OD = P * (0.6 + 1/tan(radians(180/N)))
|
||||
|
||||
# The sprocket tooth gullet consists of four segments
|
||||
x0 = 0
|
||||
y0 = PD/2 - R
|
||||
|
||||
# ---- Segment 1 -----
|
||||
alpha = 35 + 60/N
|
||||
x1 = -R * cos(radians(alpha))
|
||||
y1 = PD/2 - R * sin(radians(alpha))
|
||||
arc_end = [x1, y1]
|
||||
|
||||
# ---- Segment 2 -----
|
||||
alpha = 35 + 60/N
|
||||
beta = 18 - 56 / N
|
||||
x2 = M - E * cos(radians(alpha-beta))
|
||||
y2 = T - E * sin(radians(alpha-beta)) + PD/2
|
||||
|
||||
# # ---- Segment 3 -----
|
||||
y2o = y2 - PD/2
|
||||
hyp = sqrt((-W-x2)**2 + (-V-y2o)**2)
|
||||
AP = sqrt(hyp**2 - F**2)
|
||||
gamma = atan((y2o + V)/(x2 + W))
|
||||
alpha = asin(AP / hyp)
|
||||
beta = 180 - (90 - degrees(alpha)) - (90 - degrees(gamma))
|
||||
x3o = AP * sin(radians(beta))
|
||||
y3o = AP * cos(radians(beta))
|
||||
x3 = x2 - x3o
|
||||
y3 = y2 + y3o
|
||||
|
||||
# ---- Segment 4 -----
|
||||
alpha = 180/N
|
||||
m = -1/tan(radians(alpha))
|
||||
yf = PD/2 - V
|
||||
A = 1 + m**2
|
||||
B = 2*m*yf - 2*W
|
||||
C = W**2 + yf**2 - F**2
|
||||
# x4a = (-B - sqrt(B**2 - 4 * A * C)) / (2*A)
|
||||
x4b = (-B + sqrt(B**2 - 4 * A * C)) / (2*A)
|
||||
x4 = -x4b
|
||||
y4 = m * x4
|
||||
|
||||
p0 = [x0,y0]
|
||||
p1 = [x1,y1]
|
||||
p2 = [x2,y2]
|
||||
p3 = [x3,y3]
|
||||
p4 = [x4,y4]
|
||||
p5 = [-x1,y1]
|
||||
p6 = [-x2,y2]
|
||||
p7 = [-x3,y3]
|
||||
p8 = [-x4,y4]
|
||||
|
||||
w.move(p4) # vectors are lists [x,y]
|
||||
w.arc(p3, F, 0)
|
||||
w.line(p2)
|
||||
w.arc(p1, E, 1)
|
||||
w.arc(p0, R, 1)
|
||||
|
||||
# ---- Mirror -----
|
||||
w.arc(p5, R, 1)
|
||||
w.arc(p6, E, 1)
|
||||
w.line(p7)
|
||||
w.arc(p8, F, 0)
|
||||
|
||||
# ---- Polar Array ----
|
||||
alpha = -radians(360/N)
|
||||
for n in range(1,N):
|
||||
# falling gullet slope
|
||||
w.arc(rotate(p3, alpha*n), F, 0)
|
||||
w.line(rotate(p2, alpha*n))
|
||||
w.arc(rotate(p1, alpha*n), E, 1)
|
||||
w.arc(rotate(p0, alpha*n), R, 1)
|
||||
|
||||
# rising gullet slope
|
||||
w.arc(rotate(p5, alpha*n), R, 1)
|
||||
w.line(rotate(p6, alpha*n))
|
||||
w.arc(rotate(p7, alpha*n), E, 0)
|
||||
w.arc(rotate(p8, alpha*n), F, 0)
|
||||
|
||||
w.close()
|
||||
|
||||
return w
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user