114 Commits

Author SHA1 Message Date
forbes
ca2236c614 chore: add .mailmap to normalize git identity
Some checks failed
Pylint / build (macOs-latest) (push) Waiting to run
Pylint / build (windows-latest) (push) Waiting to run
Pylint / build (ubuntu-latest) (push) Failing after 17s
2026-03-03 14:17:27 -06:00
forbes-0023
1e26c393f3 feat: add Kindred Create integration
- Replace package.xml with Kindred-extended manifest (priority 40, sdk dependency)
- Add Init.py: console bootstrap with sys.path setup for pygears/namespace package
- Add InitGui.py: workbench import + gear command injection into PartDesign contexts
2026-02-28 15:16:50 -06:00
github-actions[bot]
16858bea80 chore: update reference images from CI [update-references workflow] 2026-02-14 20:53:04 +00:00
Lorenz Lechner
d5661c81d3 README, projekt001/002, pytest filterwarnings, lint CI
- README: Liberapay entfernt, Pixi-Commands aufgelistet
- projekt_001 -> projekt001 (Umbenennung)
- projekt002: zweiter Visual-Test mit 4 Ansichten (metafile.yaml, README)
- pyproject.toml: filterwarnings für SWIG/FreeCAD DeprecationWarnings
- .github: lint mit || true (schlägt nie fehl)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:48:29 +01:00
Lorenz Lechner
64ecd2f6b1 fix numpy version 2026-02-14 21:26:42 +01:00
Lorenz Lechner
f7e43a2a4f do not fail on lint 2026-02-14 21:10:38 +01:00
Lorenz Lechner
ef21205ed4 update pixi version 2026-02-14 21:04:36 +01:00
Lorenz Lechner
be58b5856f tests: pytest, visual tests (projekt_001), CI (pylint+visual, update-references)
- Switch from unittest to pytest (test_gears.py, xfail for OCC helical extrusion)
- Add visual regression tests (freecad.visual_tests): test_visual_projects.py, projekt_001
- Pixi: test/test-visual/test-visual-xvfb/create-references/clean-test, create-references-xvfb
- GitHub: Pylint workflow on push, pull_request, workflow_dispatch; visual tests on Linux (xvfb)
- GitHub: Update reference images workflow (workflow_dispatch)
- setup-pixi v0.9.4, pixi v0.44.0, checkout v4
- .gitignore: artifacts/, .pytest_exitstatus

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:02:35 +01:00
Robert Stancil
b97609a78a check gitver is castable before casting to int 2026-02-08 23:55:40 +01:00
Chris Bruner
8afd36b596 GearConnection and ChainConnection updated.
ChainConnection can connect a gearConnection to a gear.
2025-11-24 22:05:21 +01:00
Chris Bruner
152a70a4eb Gear and Chain Connector getting closer 2025-11-24 22:05:21 +01:00
Chris Bruner
9ed85f6766 partial fix to gear connectors 2025-11-24 22:05:21 +01:00
Chris Bruner
649c7cfb7c Fix GearConnector circular recompute dependency
- Call purgeTouched() on slave gear after Placement modifications
  - Mark computed properties as Output (flag 8) instead of read-only (flag 1)
    to prevent triggering recomputes when set during execute()

  Resolves infinite recompute loop and "still touched after recompute" warning.
2025-11-24 22:05:21 +01:00
Chris Bruner
33cbe81938 Fix GearConnector AttributeError: replace dw with pitch_diameter
The obsolete 'dw' property was removed from gear objects but GearConnector
  was still accessing it, causing AttributeError when connecting gears.
  Replaced all instances with 'pitch_diameter.Value' property.
2025-11-24 22:05:21 +01:00
theo-vt
89acd917ae Open and commit transaction on gear creation 2025-09-13 10:39:41 +02:00
hasecilu
0c8d8d764b Fix typos
"anggle" -> "angle"
"thicknes" -> "thickness"

Spotten on CrowdIn, with help of Kay G
2025-01-09 00:09:16 +01:00
hasecilu
f2d249c162 fixup! Update translations from CrowdIn 2025-01-06 21:29:28 +01:00
hasecilu
aa7a7486df Update translations from CrowdIn 2025-01-06 21:29:28 +01:00
looooo
8c7dfc9f0b worm-notebook update min_ground 2025-01-04 23:31:30 +01:00
looooo
e65f2e8924 update jupyter-notebook for the construction of the teeth-flanks of a worm gear 2025-01-04 22:02:47 +01:00
looooo
7034963050 refactor: beta->helix-angle 2025-01-04 12:44:08 +01:00
looooo
3707c2718e cycloide gear: dw -> pitch_diameter 2025-01-04 00:01:26 +01:00
looooo
f7c8080f5c rename: dw=pitch_diameter, da=addendum_diameter, df=root_diameter 2025-01-03 14:10:17 +01:00
looooo
5d3072ab4a examples: restructure 2024-12-26 12:32:11 +01:00
Orhan "aib" Kavrakoglu
284e2881b6 Extend axle and offset holes past the limits of the gear. Fixes #196 2024-12-25 22:13:55 +01:00
looooo
f473737f8d worm_cuttin: another attempt 2024-12-25 12:36:07 +01:00
looooo
e117b916b7 worm notebook 2024-12-24 19:31:14 +01:00
looooo
d7b4df66f2 examples: Drawings: InvoluteGear 2024-12-23 17:21:58 +01:00
looooo
e7f4a4c3cf examples: add drawing for involute gear 2024-12-23 13:43:17 +01:00
looooo
f11060bff7 rack-drawing: fix typo 2024-12-22 22:59:03 +01:00
looooo
32c4e8ec73 add Drawings for a gear-rack 2024-12-22 22:56:25 +01:00
looooo
952388ac0e README.md: update dependencies 2024-12-21 13:38:59 +01:00
looooo
790c8371c6 pixi: update 2024-12-21 13:36:34 +01:00
looooo
359c985aea pyproject.toml: remove pixi from pyproject.toml 2024-12-21 13:34:38 +01:00
looooo
10f68c4dd7 make hole properties optional (backward-compatibility 2024-12-18 22:03:15 +01:00
looooo
fe3b945dfe fp -> obj 2024-12-18 21:49:23 +01:00
Lorenz Lechner
26e8703270 gh-action build matrix 2024-12-08 14:22:55 +01:00
Lorenz Lechner
df9d17e975 gh-action add test 2024-12-08 14:21:20 +01:00
Lorenz Lechner
156359c6e0 gh-action: add test 2024-12-08 12:56:03 +01:00
Lorenz Lechner
44bd718202 pixi: add test 2024-12-08 12:55:09 +01:00
looooo
63a399cc88 gh-action fix 2024-12-08 00:32:14 +01:00
looooo
6dea1e4162 pixi: add pylint 2024-12-08 00:30:44 +01:00
looooo
ed1640fec4 pixi:update readme 2024-12-08 00:24:27 +01:00
looooo
7cccdcf279 min teeth = 1 2024-12-08 00:23:44 +01:00
looooo
ea679dd671 add tranformations 2024-12-07 23:58:58 +01:00
looooo
6bcec43207 add pixi 2024-12-07 23:58:09 +01:00
lorenz
c5af6e1f44 group hole parameters: hole 2024-12-01 10:28:06 +01:00
Chris Bruner
b5121b1dfe Added axle hole and offset hole possiblities to involute gears,
features added  Axle_hole Y/N, Axle size diam, offset hole Y/N offset
amount, and offset size. This allows a crank type system as well.
2024-12-01 10:20:38 +01:00
hasecilu
09b8d7666f Apply constraint on the head/root fillets of the gears
- Use `App::PropertyFloatConstraint` instead of `App::PropertyFloat`
  in order to get a smaller set of valid options for the parts.

Minimum, maximum, step and default values are set.

Previously you could enter negative numbers which were fixed at the time
of generating the shape avoiding problems but the properties were not
fixed. Now users are not able to enter bad data which leads to a
simplification of the sanitization code.

Proposed upper limit at 1,000. Users are crazy.
2024-11-17 12:35:59 +01:00
hasecilu
91206aecdd Apply constraint on the number of teeth of the gears
- Use `App::PropertyIntegerConstraint` instead of `App::PropertyInteger`
  in order to get a smaller set of valid options for the parts.

Minimum, maximum, step and default values are set.

Previously you could enter negative numbers which were fixed at the time
of generating the shape avoiding problems but the properties were not
fixed. Now users are not able to enter bad data which leads to a
simplification of the sanitization code.

Proposed upper limit at 10,000. Users are crazy.
2024-11-17 12:35:59 +01:00
hasecilu
e6b44cfe52 Fix typo on script example 2024-11-17 11:22:36 +01:00
Lorenz Lechner
4d87de3f0c use both methods (load/__setstate__ dumps/__getstate__) available for all python versions 2024-11-10 19:51:51 +01:00
Jiao Ye
8893371ce5 Fix issue #182
da3c851d2c breaks bevel gear and it's reported in https://github.com/looooo/freecad.gears/issues/182.

Set gear.z properly to fix it.
2024-11-02 22:56:31 +01:00
Alexander Vowinkel
875d1aaf47 add scipy dependency in meta files. fixes #155 2024-11-01 16:07:51 +01:00
looooo
b907e1febb connector: teeth->num_teeth 2024-10-21 23:07:21 +02:00
looooo
795511859c move to pyproject.toml 2024-10-21 23:00:14 +02:00
Lorenz Lechner
4be061bb42 apply fix to internal involute gears 2024-10-21 22:02:45 +02:00
Lorenz Lechner
13b2a9c4a5 fix num_teeth issue 2024-10-09 19:26:51 +02:00
hasecilu
84237f0e3d Update Spanish translation 2024-10-06 00:42:00 +02:00
hasecilu
7db0fd7ec0 Enhance the translation support
Migrate some strings marked with `translate()` to use
`QT_TRANSLATE_NOOP()`
2024-10-06 00:42:00 +02:00
hasecilu
1b2981c6cf Update translation script and add locale-agnostic file
Also include `translations/README.md` file with instructions.
2024-10-06 00:42:00 +02:00
hasecilu
7911050526 Rename GUI commands to follow FreeCAD format
Making this change will enable users to use `What's this?` command on FCGear
commands and get the correct Wiki article.

Fix #151
2024-10-05 23:32:39 +02:00
hasecilu
da3c851d2c Rename teeth property to num_teeth 2024-10-05 23:30:54 +02:00
Syres916
a7f6d06072 Update version number 2024-09-18 19:33:24 +02:00
Syres916
2b235dbc2b Update version number 2024-09-18 19:33:24 +02:00
Syres916
2f33c49569 [Gears] Eliminate unwanted errors for version 1.0 2024-09-18 19:33:24 +02:00
Syres916
84d2eb8279 [Gears] Update FreeCAD version check on startup 2024-09-11 12:09:05 +02:00
Max Wilfinger
f06c63c570 change gear WB icon 2024-05-20 19:53:58 +02:00
catalintucureanu
9715c068e4 Update timinggear_t.py
fix for issue #164
2024-05-10 11:34:00 +02:00
Max Wilfinger
bc3702f213 update icons to be in line with FreeCAD 2024-04-10 18:13:49 +00:00
hasecilu
bfc91608a9 Add error handling at GearConnector 2024-03-22 16:55:16 +00:00
hasecilu
471019639f Fix missing comma
Close #154
2024-03-21 05:47:11 +00:00
lorenz
529b500d1e Update README.md 2024-02-11 19:56:47 +01:00
Chris Hennes
66de0470e4 Make license match SPDX ID 2024-02-10 15:59:26 +00:00
hasecilu
89a11603ba Update logic conditions
There is no need to compare against True or False.
Use `not` when wanting False activation.
2024-02-10 15:59:18 +00:00
hasecilu
139e0acf52 Add Spanish translation 2024-02-10 15:59:18 +00:00
hasecilu
f21eac0e69 Add translation support
- Add bash script to include marked strings on translation files (*.ts)
- translateutils to import translate() placeholder function
2024-02-10 15:59:18 +00:00
looooo
ffb4950054 set numpoints to 20 by default 2024-01-10 12:21:00 +01:00
looooo
51fe971aea fix regression of root fillet 2024-01-10 12:18:24 +01:00
looooo
3fa30b7f87 update jupyter-notebook 2024-01-10 12:15:34 +01:00
looooo
5ce0a500a4 fix regression with head fillet 2024-01-10 12:09:11 +01:00
looooo
8657be7d5c update notebook 2024-01-06 20:26:49 +01:00
looooo
1c53d09c28 update notebook 2024-01-05 18:14:10 +01:00
looooo
2136e1b4cf more docs 2024-01-04 23:41:38 +01:00
looooo
5d09b9b8f5 add some docs 2024-01-04 23:37:57 +01:00
looooo
88b0386700 update test 2024-01-04 22:51:36 +01:00
lorenz
0aaf67a2c6 Update pylint.yml 2024-01-04 22:37:03 +01:00
lorenz
d24bee2e53 Update pylint.yml 2024-01-04 22:30:26 +01:00
lorenz
2ee9521740 Update pylint.yml 2024-01-04 22:24:35 +01:00
lorenz
9d4a87b6e4 Update pylint.yml 2024-01-04 22:23:15 +01:00
lorenz
ceacf02cd6 Update pylint.yml 2024-01-04 22:21:46 +01:00
lorenz
c2472f1dcb Update pylint.yml 2024-01-04 22:19:03 +01:00
lorenz
f5cd2bf3d4 Update pylint.yml 2024-01-04 22:16:49 +01:00
looooo
246ee57d14 update gh-action 2024-01-04 20:54:04 +01:00
looooo
26a289f37e fix tests 2024-01-04 20:49:02 +01:00
lorenz
9abfe687f1 gh-action: only test py311 2024-01-04 20:25:03 +01:00
looooo
5760caf820 test: impove naming 2024-01-04 20:13:18 +01:00
looooo
dcf5edd4f0 helicalextrusion -> helical_extrusion 2024-01-04 20:10:31 +01:00
looooo
b140f621ee add test for helical extrusion 2024-01-04 20:05:39 +01:00
looooo
34bf441678 update icons 2024-01-04 15:06:48 +01:00
looooo
a6eb920643 add boolean reversed to fillet_between_edges 2024-01-04 12:44:42 +01:00
looooo
17056c1f54 add root_fillet, head_fillet, backlash to timinggear_t 2024-01-04 12:07:14 +01:00
looooo
80bf5ead66 ruff 2024-01-02 23:52:11 +01:00
looooo
323c922778 refactoring:
make rotation matrix rotating from x -> y (changing the sign of the rotation)
not sure why it was defined the other way ???
2024-01-02 23:47:53 +01:00
looooo
fd73f5e3e1 add freecad.part module 2024-01-02 23:25:10 +01:00
looooo
d8034c0a86 from freecad import part 2024-01-02 23:24:31 +01:00
looooo
5231798cad further refactoring 2024-01-02 22:07:49 +01:00
looooo
cd7d2cb9df refactoring App -> freecad.app, FreeCADGui -> freecad.gui 2024-01-02 21:56:39 +01:00
looooo
b807a703bf further refactoring 2024-01-02 21:36:07 +01:00
looooo
9c2d813332 add basegear and keep feature.py 2024-01-02 09:57:15 +01:00
looooo
c0ebab0806 Revert "rename features to basegear"
This reverts commit 878811ae54.
2024-01-02 09:57:15 +01:00
looooo
a386534b8a rename features to basegear 2024-01-02 09:57:15 +01:00
looooo
4076e566c0 ruff formating 2024-01-02 09:57:15 +01:00
looooo
fa773bbe6e refactoring (split in different files)
make explicitly dependent on scipy
2024-01-02 09:57:15 +01:00
120 changed files with 33550 additions and 8221 deletions

View File

@@ -1,23 +1,28 @@
name: Pylint
on: [push]
on:
push:
pull_request:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10"]
os: [macOs-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
- uses: actions/checkout@v4
- uses: prefix-dev/setup-pixi@v0.9.4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
pixi-version: v0.63.2
cache: false
- run: pixi run lint || true
- run: pixi run test
- name: Install xvfb (Linux)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y xvfb xauth
- name: Visual tests (Linux)
if: matrix.os == 'ubuntu-latest'
run: pixi run test-visual-xvfb

38
.github/workflows/update-references.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
# Manuell ausführen: Referenzbilder auf CI-Umgebung neu erzeugen und committen.
# So passen die Referenzen zur CI-Umgebung (FreeCAD/Python auf ubuntu-latest).
name: Update reference images
on:
workflow_dispatch:
permissions:
contents: write
jobs:
update-references:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pixi
uses: prefix-dev/setup-pixi@v0.9.4
with:
pixi-version: v0.63.2
cache: true
- name: Install dependencies
run: pixi install
- name: Install xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb xauth
- name: Generate reference images (update)
run: pixi run create-references-xvfb
- name: Commit and push reference images
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add tests/data/*/references/
git diff --staged --quiet || git commit -m "chore: update reference images from CI [update-references workflow]"
git push

10
.gitignore vendored
View File

@@ -40,6 +40,12 @@ htmlcov/
nosetests.xml
coverage.xml
# Visual test artifacts (references are versioned)
tests/data/*/artifacts/
# Pytest exit status (used by xvfb wrapper)
.pytest_exitstatus
# Translations
*.mo
*.pot
@@ -59,3 +65,7 @@ target/
.ipynb_checkpoints/
results/
*.vtk
# pixi environments
.pixi
*.egg-info

7
.mailmap Normal file
View File

@@ -0,0 +1,7 @@
forbes <contact@kindred-systems.com> forbes <joseph.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> forbes <zoe.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> forbes-0023 <joseph.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> forbes-0023 <zoe.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> josephforbes23 <joseph.forbes@kindred-systems.com>
forbes <contact@kindred-systems.com> Zoe Forbes <forbes@copernicus-9.kindred.internal>
forbes <contact@kindred-systems.com> admin <admin@kindred-systems.com>

156
README.md
View File

@@ -1,83 +1,81 @@
## A Gear module for FreeCAD
[![Liberapay](http://img.shields.io/liberapay/patrons/looooo.svg?logo=liberapay)](https://liberapay.com/looooo/donate)
# FreeCAD Gears
A gear workbench for FreeCAD: create involute, cycloid, bevel, worm, timing, lantern and crown gears with full control over parameters.
## Requirements
FreeCAD > v0.16
__python > 3 (for python2 use branch py2)__
- **FreeCAD** ≥ 1.0 (or ≥ 0.16 for older setups)
- **Python** ≥ 3.8 (used by FreeCAD)
- **Python packages:** `numpy`, `scipy`, `sympy` (optional: `jupyter`, `matplotlib`)
## Supported gear-types
## Supported gear types
### Cylindric Involute
* Shifting
* Helical
* Double Helical
* Undercut
### Cylindric involute
- Shifting, helical, double helical, undercut, fillets
![involute-gear](examples/involute-double-helical-gear.png)
![involute-gear](examples/images/involute-double-helical-gear.png)
### Involute rack
![involute-rack](examples/images/involute-rack.png)
### Involute Rack
![involute-rack](examples/involute-rack.png)
### Cylindric cycloid
- Helical, double helical, fillets
### Cylindric Cycloid
* Helical
* Double Helical
![cycloid-gear](examples/images/cycloid-gear.png)
![cycloid-gear](examples/cycloid-gear.png)
### Cycloid rack
![cycloid-rack](examples/images/cycloid-rack.png)
### Cycloid Rack
### Spherical involute bevel gear
- Spiral
![cycloid-rack](examples/cycloid-rack.png)
![bevel-gear](examples/images/bevel-gear.png)
### Spherical Involute Bevel-Gear
* Spiral
### Crown gear
![crown-gear](examples/images/crown-gear.png)
![bevel-gear](examples/bevel-gear.png)
### Worm gear
![worm-gear](examples/images/worm-gear.png)
### Crown-Gear
![crown-gear](examples/crown-gear.png)
### Timing gear
![timing-gear](examples/images/timing-gear.png)
### Worm-Gear
![worm-gear](examples/worm-gear.png)
### Lantern gear
![lantern-gear](examples/images/lantern-gear.png)
### Timing-Gear
![timing-gear](examples/timing-gear.png)
### Lantern-Gear
![lantern-gear](examples/lantern-gear.png)
---------------------------
---
## Installation
### Addon Manager
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.
### Addon Manager (recommended)
In FreeCAD: **Tools****Addon Manager** → search for “Gears” (or “FCGear”) → Install.
### pip
```bash
pip install freecad.gears
```
Or from source:
```bash
pip install https://github.com/looooo/freecad.gears/archive/master.tar.gz
```
Use the same Python/pip that FreeCAD uses on your system.
`pip install https://github.com/looooo/FCGear/archive/master.tar.gz`
**Important note:** Most systems have multiple versions of python installed. Make sure the `pip` you're using is used by FreeCAD as well.
---
## 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:
### In FreeCAD
1. Open FreeCAD and switch to the **Gear** workbench.
2. **File****New** (or open a document).
3. Create a gear from the toolbar and adjust parameters in the property panel.
### From Python
```python
import FreeCAD as App
import freecad.gears.commands
gear = freecad.gears.commands.CreateInvoluteGear.create()
gear.teeth = 20
gear.num_teeth = 20
gear.beta = 20
gear.height = 10
gear.double_helix = True
@@ -85,17 +83,57 @@ App.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
```
---
## Development
The project uses [pixi](https://pixi.sh/) for environment and task management.
### Setup
```bash
pixi install
```
### Pixi commands
### Tasks (Kurzreferenz)
| Befehl | Beschreibung |
|--------|--------------|
| `pixi run freecad` | FreeCAD mit freecad.gears starten. |
| `pixi run lint` | Pylint. |
| `pixi run test` | Unit-Tests. |
| `pixi run test-visual` | Visual-Tests (Display nötig). |
| `pixi run test-visual-xvfb` | Visual-Tests unter xvfb. |
| `pixi run test-all` | Alle Tests. |
| `pixi run create-references` | Referenzbilder erzeugen. |
| `pixi run create-references-xvfb` | Referenzbilder unter xvfb. |
| `pixi run clean-test` | Test-Artefakte und Referenzen löschen. |
Visual tests use [freecad.visual_tests](https://github.com/looooo/freecad.visual_tests): each project under `tests/data/*/` has a `metafile.yaml` and a `.FCStd` model; references are stored in `references/`.
### CI (GitHub Actions)
- **Pylint:** Runs on push, pull_request and `workflow_dispatch` (lint does not fail the job).
- **Tests:** Unit tests on all OS; visual tests (xvfb) on Ubuntu only.
- **Update reference images:** Manual workflow “Update reference images” to regenerate references on CI and push them to the repo.
---
## 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))
- Elements of Metric Gear Technology ([PDF](http://qtcgears.com/tools/catalogs/PDF_Q420/Tech.pdf))
### FreeCAD Forum
- [Involute gear generator preview](https://forum.freecadweb.org/viewtopic.php?f=10&t=4829)
- [Bevel gear module/script/tutorial](https://forum.freecadweb.org/viewtopic.php?f=3&t=12878)
- [Gears in FreeCAD: FC Gear](https://forum.freecadweb.org/viewtopic.php?f=24&t=27381)
- [FC Gears: Feedback thread](https://forum.freecadweb.org/viewtopic.php?f=8&t=27626)
Please check the [issue tracker](https://github.com/looooo/freecad.gears/issues) before opening a new report.
---
## License
# License
GNU General Public License v3.0

View File

@@ -1,6 +1,6 @@
#TODO:
# TODO
## refactoring
- [ ] fp.gear.z -> fp.gear.num_teeth
- [ ] fp.teeth -> fp.gear.num_teeth
- [X] fp.gear.z -> fp.gear.num_teeth
- [X] fp.teeth -> fp.gear.num_teeth

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 205 KiB

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View File

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 188 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

View File

@@ -11,13 +11,20 @@ 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')
gui.activeDocument().activeView().saveImage(
"/home/lo/Schreibtisch/animated_gear/gear_{}.png".format(i),
300,
300,
"Current",
)
def update(*args):
print("time")
@@ -25,5 +32,6 @@ def update(*args):
g1.Placement.Rotation.Angle += delta_phi * 2
g2.Placement.Rotation.Angle -= delta_phi
timer.timeout.connect(update)
timer.start()

File diff suppressed because one or more lines are too long

View File

@@ -14,68 +14,24 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 1,
"id": "7eacf041-aa83-49e2-9cbe-066f177197f6",
"metadata": {},
"outputs": [],
"source": [
"import sympy as sp\n",
"import numpy as np"
"import sympy as sym\n",
"import numpy as np\n",
"from pygears.transformation import symbolic_transformation, numeric_transformation"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "980417d0-c79d-4501-a7cc-9725b3bbea83",
"metadata": {},
"outputs": [],
"source": [
"def symbolic_transformation(angle, axis, translation=np.array([0., 0., 0.])):\n",
" \"\"\"\n",
" see http://en.wikipedia.org/wiki/SO%284%29#The_Euler.E2.80.93Rodrigues_formula_for_3D_rotations\n",
" sympy enabled transformation\n",
" angle: angle of rotation\n",
" axis: the axis of the rotation\n",
" translation: translation of transformation\n",
" \"\"\"\n",
" assert len(axis) == 3\n",
" a = sp.cos(angle / 2)\n",
" axis_normalized = axis / sp.sqrt(axis.dot(axis))\n",
" (b, c, d) = -axis_normalized * sp.sin(angle / 2)\n",
" mat = sp.Matrix(\n",
" [\n",
" [\n",
" a**2 + b**2 - c**2 - d**2,\n",
" 2 * (b * c - a * d),\n",
" 2 * (b * d + a * c),\n",
" translation[0],\n",
" ],\n",
" [\n",
" 2 * (b * c + a * d),\n",
" a**2 + c**2 - b**2 - d**2,\n",
" 2 * (c * d - a * b),\n",
" translation[1],\n",
" ],\n",
" [\n",
" 2 * (b * d - a * c),\n",
" 2 * (c * d + a * b),\n",
" a**2 + d**2 - b**2 - c**2,\n",
" translation[2],\n",
" ],\n",
" [0.0, 0.0, 0.0, 1.0],\n",
" ]\n",
" )\n",
" return sp.simplify(mat)\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 2,
"id": "5852aa56-e66b-4f3c-a50d-0cb4cb21abd2",
"metadata": {},
"outputs": [],
"source": [
"t = sp.Symbol(\"t\")\n",
"t = sym.Symbol(\"t\")\n",
"T1 = symbolic_transformation(np.pi / 2.,\n",
" np.array([1., 0., 0.]),\n",
" np.array([12.5,0., 1.15]))\n",
@@ -86,12 +42,12 @@
" np.array([1., 0., 0.]),\n",
" np.array([0., 0., t]))\n",
"\n",
"T = sp.nsimplify(T2.inv() @ T1.inv() @ T3, tolerance=10e-16)"
"T = sym.nsimplify(T2.inv() @ T1.inv() @ T3, tolerance=10e-16)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 3,
"id": "7c9837b8-caf7-4447-bf0d-eba9085197a5",
"metadata": {},
"outputs": [
@@ -104,14 +60,14 @@
" [ 0. , 0. , 0. , 0. ]])"
]
},
"execution_count": 8,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"T_fn = sp.lambdify(t, T)\n",
"dT_fn = sp.lambdify(t, T.diff(t))\n",
"T_fn = sym.lambdify(t, T)\n",
"dT_fn = sym.lambdify(t, T.diff(t))\n",
"dT_fn(0.)"
]
},
@@ -178,11 +134,11 @@
"5. erstellen einer B-Spline Kurve welche durch die Kinematik T transformiert wird\n",
"\n",
"```python\n",
"c_1 = Part.BSplineCurve()\n",
"c_1 = part.BSplineCurve()\n",
"c_1.interpolate(Points=xyz_1)\n",
"c_1 = c_1.toShape()\n",
"\n",
"Part.show(c_1.transformShape(T))\n",
"part.show(c_1.transformShape(T))\n",
"```\n",
"\n",
"6. Loft anwenden auf die erstellten BSpline Kurven\n",
@@ -196,94 +152,128 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 27,
"id": "cfd8026b-5a84-4882-a1de-63580776a579",
"metadata": {},
"outputs": [],
"source": [
"import sympy as sp\n",
"t, x, z = sp.symbols([\"t\", \"x\", \"z\"], real=True)\n",
"s, alpha, n_t, y = sp.symbols([\"s\", \"alpha\", \"n_t\", \"y\"], real=True, positiv=True)"
"import sympy as sym\n",
"import numpy as np\n",
"import scipy as sp\n",
"\n",
"from pygears.transformation import symbolic_transformation, numeric_transformation\n",
"\n",
"t, x, z, m, r_w = sym.symbols([\"t\", \"x\", \"z\", \"m\", \"r_w\"], real=True)\n",
"s, alpha, n_t, y, phi = sym.symbols([\"s\", \"alpha\", \"n_t\", \"y\", \"phi\"], real=True, positiv=True)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "65bb90d7-0f5b-410e-9a3a-0f9953a4d846",
"execution_count": 9,
"id": "618dd2ae-e8e5-430d-8c8c-8983358334b7",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}\\cos{\\left(\\frac{n_{t} t}{\\pi} \\right)} & \\sin{\\left(\\frac{n_{t} t}{\\pi} \\right)} & 0 & 0\\\\- \\sin{\\left(\\frac{n_{t} t}{\\pi} \\right)} & \\cos{\\left(\\frac{n_{t} t}{\\pi} \\right)} & 0 & 0\\\\0 & 0 & 1 & n_{t} t\\\\0 & 0 & 0 & 1.0\\end{matrix}\\right]$"
"$\\displaystyle \\left[\\begin{matrix}1 & 0 & 0 & r_{w}\\\\0 & 1 & 0 & 0\\\\0 & 0 & 1 & 0\\\\0.0 & 0.0 & 0.0 & 1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[ cos(n_t*t/pi), sin(n_t*t/pi), 0, 0],\n",
"[-sin(n_t*t/pi), cos(n_t*t/pi), 0, 0],\n",
"[ 0, 0, 1, n_t*t],\n",
"[ 0, 0, 0, 1.0]])"
"[ 1, 0, 0, r_w],\n",
"[ 0, 1, 0, 0],\n",
"[ 0, 0, 1, 0],\n",
"[0.0, 0.0, 0.0, 1.0]])"
]
},
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"T_spiral = symbolic_transformation(t * n_t / sp.pi, np.array([0, 0, 1]), np.array([0, 0, t * n_t]))\n",
"T_spiral"
"p0 = symbolic_transformation(0,np.array([0, 0, 1]), [r_w, 0, 0, 1])\n",
"p0"
]
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 11,
"id": "0f305b8b-0fb5-4b71-80b6-9a2b43b59e26",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}0\\\\s \\cos{\\left(\\alpha \\right)}\\\\s \\sin{\\left(\\alpha \\right)}\\\\1\\end{matrix}\\right]$"
"$\\displaystyle \\left[\\begin{matrix}r_{w} + s \\cos{\\left(\\alpha \\right)}\\\\0\\\\- s \\sin{\\left(\\alpha \\right)}\\\\1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[r_w + s*cos(alpha)],\n",
"[ 0],\n",
"[s*cos(alpha)],\n",
"[s*sin(alpha)],\n",
"[ 1]])"
"[ -s*sin(alpha)],\n",
"[ 1.0]])"
]
},
"execution_count": 22,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"l = sp.Matrix([0, s * sp.cos(alpha), s * sp.sin(alpha), 1])\n",
"l = p0 @ sym.Matrix([s * sym.cos(alpha), 0, -s * sym.sin(alpha), 1])\n",
"l"
]
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 19,
"id": "65bb90d7-0f5b-410e-9a3a-0f9953a4d846",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}\\cos{\\left(\\phi \\right)} & \\sin{\\left(\\phi \\right)} & 0 & 0\\\\- \\sin{\\left(\\phi \\right)} & \\cos{\\left(\\phi \\right)} & 0 & 0\\\\0 & 0 & 1 & m n_{t} \\phi\\\\0.0 & 0.0 & 0.0 & 1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[ cos(phi), sin(phi), 0, 0],\n",
"[-sin(phi), cos(phi), 0, 0],\n",
"[ 0, 0, 1, m*n_t*phi],\n",
"[ 0.0, 0.0, 0.0, 1.0]])"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"T_spiral = symbolic_transformation(phi, np.array([0, 0, 1]), np.array([0, 0, m * phi * n_t]))\n",
"T_spiral"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "da3c8575-99ad-4258-8734-c165ea65b014",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}s \\sin{\\left(\\frac{n_{t} t}{\\pi} \\right)} \\cos{\\left(\\alpha \\right)}\\\\s \\cos{\\left(\\alpha \\right)} \\cos{\\left(\\frac{n_{t} t}{\\pi} \\right)}\\\\n_{t} t + s \\sin{\\left(\\alpha \\right)}\\\\1.0\\end{matrix}\\right]$"
"$\\displaystyle \\left[\\begin{matrix}\\left(r_{w} + s \\cos{\\left(\\alpha \\right)}\\right) \\cos{\\left(\\phi \\right)}\\\\- \\left(r_{w} + s \\cos{\\left(\\alpha \\right)}\\right) \\sin{\\left(\\phi \\right)}\\\\1.0 m n_{t} \\phi - s \\sin{\\left(\\alpha \\right)}\\\\1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[s*sin(n_t*t/pi)*cos(alpha)],\n",
"[s*cos(alpha)*cos(n_t*t/pi)],\n",
"[ n_t*t + s*sin(alpha)],\n",
"[ (r_w + s*cos(alpha))*cos(phi)],\n",
"[-(r_w + s*cos(alpha))*sin(phi)],\n",
"[ 1.0*m*n_t*phi - s*sin(alpha)],\n",
"[ 1.0]])"
]
},
"execution_count": 23,
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
@@ -295,17 +285,154 @@
},
{
"cell_type": "code",
"execution_count": 32,
"execution_count": 21,
"id": "e47eb83b-6e89-4246-a82a-bd5629aedc2a",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\frac{\\pi \\operatorname{asin}{\\left(\\frac{x}{s \\cos{\\left(\\alpha \\right)}} \\right)}}{n_{t}}$"
"$\\displaystyle \\operatorname{acos}{\\left(\\frac{x}{r_{w} + s \\cos{\\left(\\alpha \\right)}} \\right)}$"
],
"text/plain": [
"pi*asin(x/(s*cos(alpha)))/n_t"
"acos(x/(r_w + s*cos(alpha)))"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_cross_section = sym.simplify(sym.solve(spiral[0] - x, phi)[1])\n",
"x_cross_section"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "c2954b39-eea0-4e27-987f-07a5d0dedaad",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}x\\\\- \\sqrt{- \\frac{x^{2} - \\left(r_{w} + s \\cos{\\left(\\alpha \\right)}\\right)^{2}}{\\left(r_{w} + s \\cos{\\left(\\alpha \\right)}\\right)^{2}}} \\left(r_{w} + s \\cos{\\left(\\alpha \\right)}\\right)\\\\1.0 m n_{t} \\operatorname{acos}{\\left(\\frac{x}{r_{w} + s \\cos{\\left(\\alpha \\right)}} \\right)} - s \\sin{\\left(\\alpha \\right)}\\\\1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[ x],\n",
"[-sqrt(-(x**2 - (r_w + s*cos(alpha))**2)/(r_w + s*cos(alpha))**2)*(r_w + s*cos(alpha))],\n",
"[ 1.0*m*n_t*acos(x/(r_w + s*cos(alpha))) - s*sin(alpha)],\n",
"[ 1.0]])"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spiral_x = sym.simplify(spiral.subs({phi: x_cross_section}))\n",
"spiral_x"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "268c6302-4e7d-45e0-be28-1b64cf8b766e",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\frac{- r_{w} + \\sqrt{x^{2} + y^{2}}}{\\cos{\\left(\\alpha \\right)}}$"
],
"text/plain": [
"(-r_w + sqrt(x**2 + y**2))/cos(alpha)"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y_cross_section = sym.simplify(sym.solve(spiral_x[1]- y, s)[0])\n",
"y_cross_section\n",
"y_cross_section = (sym.sqrt(x**2 + y**2) - r_w) / sym.cos(alpha)\n",
"y_cross_section"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "7284bd6a-b47b-483c-8e9f-79fcaecce5e3",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}x\\\\- y\\\\1.0 m n_{t} \\operatorname{acos}{\\left(\\frac{x}{r{\\left(x,y \\right)}} \\right)} + 1.0 r_{w} \\tan{\\left(\\alpha \\right)} - 1.0 r{\\left(x,y \\right)} \\tan{\\left(\\alpha \\right)}\\\\1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[ x],\n",
"[ -y],\n",
"[1.0*m*n_t*acos(x/r(x, y)) + 1.0*r_w*tan(alpha) - 1.0*r(x, y)*tan(alpha)],\n",
"[ 1.0]])"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f_r = sym.Function(\"r\")(x, y)\n",
"spiral_xy = sym.simplify(spiral_x.subs({s: y_cross_section}))\n",
"spiral_xy = spiral_xy.subs({sym.Abs(y): y, sym.sqrt(x**2 + y**2): f_r})\n",
"spiral_xy"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "e7b9f7cc-59f0-4dc6-922c-c94a753c0fd5",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle z{\\left(x,y \\right)}$"
],
"text/plain": [
"z(x, y)"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"z = sym.Function(\"z\")(x, y)\n",
"z"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "080e1845-8ff4-493e-aa28-5b677bfa3cb4",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\frac{y - y_{p} + z{\\left(x,y \\right)} \\frac{\\partial}{\\partial y} z{\\left(x,y \\right)}}{\\sqrt{\\left(y - y_{p}\\right)^{2} + z^{2}{\\left(x,y \\right)}}}$"
],
"text/plain": [
"(y - y_p + z(x, y)*Derivative(z(x, y), y))/sqrt((y - y_p)**2 + z(x, y)**2)"
]
},
"execution_count": 32,
@@ -314,97 +441,330 @@
}
],
"source": [
"x_cross_section = sp.simplify(sp.solve(spiral[0] - x, t)[1])\n",
"x_cross_section # parameter s"
"y_p =sym.Symbol(\"y_p\")\n",
"dist_p = sym.sqrt(z**2 + (y - y_p)**2)\n",
"dist_p.diff(y)"
]
},
{
"cell_type": "markdown",
"id": "d9559950-1520-439c-8e7a-35b4006f104a",
"metadata": {},
"source": [
"## for x = 0, for a first guess of t"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "c2954b39-eea0-4e27-987f-07a5d0dedaad",
"execution_count": 1,
"id": "0811a6d6-4544-4a99-b4bd-ba5cf6b9027e",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}x\\\\s \\sqrt{1 - \\frac{x^{2}}{s^{2} \\cos^{2}{\\left(\\alpha \\right)}}} \\cos{\\left(\\alpha \\right)}\\\\s \\sin{\\left(\\alpha \\right)} + \\pi \\operatorname{asin}{\\left(\\frac{x}{s \\cos{\\left(\\alpha \\right)}} \\right)}\\\\1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[ x],\n",
"[s*sqrt(1 - x**2/(s**2*cos(alpha)**2))*cos(alpha)],\n",
"[ s*sin(alpha) + pi*asin(x/(s*cos(alpha)))],\n",
"[ 1.0]])"
"name": "stdout",
"output_type": "stream",
"text": [
"PATH_TO_FREECAD_LIBDIR not specified, using default FreeCAD version in /Users/lo/mambaforge/envs/freecad/lib\n"
]
},
"execution_count": 33,
{
"data": {
"text/plain": [
"<Part::PartFeature>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spiral_x = sp.simplify(spiral.subs({t: x_cross_section}))\n",
"spiral_x"
"import scipy as sp\n",
"import numpy as np\n",
"from freecad import part\n",
"from freecad import app\n",
"from pygears.transformation import numeric_transformation\n",
"\n",
"debug = False\n",
"def compute_involute(module=1, teeth=15, height=5, worm_pitch_diameter=10, num_threads=1, alpha=np.deg2rad(20)):\n",
" x = 0.\n",
" r_w = module * teeth / 2\n",
" x_p = worm_pitch_diameter / 2\n",
" r_thales = r_w / 2.\n",
" x_thales = y_p + r_thales\n",
" \n",
" def length(y):\n",
" return (x**2 + y**2)**(0.5)\n",
" \n",
" def dlength_dy(y):\n",
" return y / length(y)\n",
" \n",
" def z(y, t):\n",
" r = length(y)\n",
" return - module * num_threads * np.arcsin(x / r) / 2 + (r-r_w) * np.tan(alpha) + t\n",
" \n",
" def dz_dy(y):\n",
" r = length(y)\n",
" return module * num_threads * x * dlength_dy(y) / \\\n",
" (2 * np.sqrt(1 - x ** 2 / r ** 2 ) * r ** 2) + \\\n",
" np.tan(alpha) * dlength_dy(y)\n",
" \n",
" def distance_yp(y, t):\n",
" return np.sqrt((y_p - y) ** 2 + z(y, t) ** 2)\n",
"\n",
" def distance_yp_2(y, t):\n",
" return (y_p - y) ** 2 + z(y, t) ** 2\n",
" \n",
" def ddistance_yp_dy(y, t):\n",
" return (y - y_p + z(y, t) * dz_dy(y)) / distance_yp(y, t)\n",
" \n",
" def distance_ythales(y, t):\n",
" return np.sqrt((y_thales - y) ** 2 + z(y, t) ** 2)\n",
" \n",
" def min_ground(pars):\n",
" y, t = pars\n",
" # return the normal-condition + the intesection of the tooth-flank with the thales circle\n",
" return ddistance_yp_dy(y, t) ** 2 + (distance_ythales(y, t) - r_thales) ** 2\n",
"\n",
" def min_head(pars):\n",
" y, t = pars\n",
" r_0 = y_p - 5 * module # * (1 + clearence)\n",
" # y_inner = r_0 * np.cos(np.arcsin(x / r_0))\n",
" return ddistance_yp_dy(y, t) ** 2 + (y - r_0) ** 2\n",
"\n",
" def analytic_solution_for_x_0():\n",
" t = - (r_w + y_p) * np.tan(alpha)\n",
" y = y_p + r_w * np.sin(alpha) ** 2\n",
" return np.array([y, t])\n",
" \n",
" start = analytic_solution_for_x_0()\n",
" if debug:\n",
" print(f\"analytic solution: {analytic_solution_for_x_0()}\")\n",
" print(f\"min_ground analytic: {min_ground(start)}\")\n",
" print(f\"thales analytic: {distance_ythales(start[0], start[1]) - r_thales}\")\n",
" print(f\"normal analytic: {ddistance_yp_dy(start[0], start[1])}\")\n",
" print()\n",
"\n",
" # t_end is once computed for x=0\n",
" t_end = sp.optimize.minimize(min_head, [y_p, 0.]).x[1]\n",
"\n",
" xyz = []\n",
" for x in np.linspace(-height / 2, height / 2, 20):\n",
" xyz_section = []\n",
" t_start = sp.optimize.minimize(min_ground, start).x[1]\n",
" for t in np.linspace(t_start, t_end, 20):\n",
"\n",
" # compute the time (t) dependent transformation\n",
" phi = np.pi / 2\n",
" phi += y_p * np.tan(alpha) / r_w\n",
" phi += - np.sign(alpha) * module * np.pi / 4. / r_w\n",
" phi += t / r_w\n",
" T_0 = numeric_transformation(phi, np.array([0., 0., 1.]))\n",
" T_1 = numeric_transformation(-np.pi / 2, np.array([0., 1., 0.]))\n",
" T_2 = numeric_transformation(0., np.array([0., 0., 1.]), np.array([0, y_p + r_w, 0.]))\n",
" T = np.linalg.inv(T_2 @ T_1 @ T_0)\n",
" \n",
" # find point on curve for given t\n",
" y = sp.optimize.root(ddistance_yp_dy, y_p, (t)).x[0]\n",
" z_i = z(y, t) # - y_p * np.tan(alpha) + np.sign(alpha) * module * np.pi / 4\n",
" point = np.array([x, y, z_i, 1.])\n",
" xyz_section.append((T @ point)[:3])\n",
" xyz.append(np.array(xyz_section))\n",
"\n",
" return np.array(xyz)\n",
"\n",
"# parameters\n",
"module = 1.\n",
"teeth = 50\n",
"height = 5\n",
"worm_pitch_diameter = 10\n",
"num_threads = 1\n",
"alpha = np.deg2rad(20)\n",
"y_p = worm_pitch_diameter / 2\n",
"r_w = teeth * module / 2\n",
"clearence = 0.25\n",
"head = 0.\n",
" \n",
"# create two surfaces one for positive alpha and one for negative alpha\n",
"for alpha_i in [-alpha, alpha]: \n",
" curves = []\n",
" xyz = compute_involute(\n",
" module=module,\n",
" teeth=teeth, \n",
" height=height,\n",
" worm_pitch_diameter=worm_pitch_diameter,\n",
" num_threads=num_threads,\n",
" alpha=alpha_i)\n",
" \n",
" for line in xyz.transpose(1, 0, 2):\n",
" bs = part.BSplineCurve()\n",
" points = [app.Vector(*point) for point in line]\n",
" bs.interpolate(points)\n",
" curves.append(bs.toShape())\n",
" part.show(part.makeLoft(curves))\n",
"\n",
"# create cutting surfaces for head and bottom\n",
"r_head = y_p - module * (1 + head)\n",
"r_foot = y_p + module * (1 + clearence)\n",
"\n",
"phi_head = np.arcsin(height / 2 / r_head) # from + phi to - phi\n",
"phi_foot = np.arcsin(height / 2 / r_foot)\n",
"\n",
"line_head = []\n",
"for phi_i in np.linspace(-phi_head, phi_head, 10):\n",
" x = r_w + y_p - np.cos(phi_i) * r_head\n",
" z = np.sin(phi_i) * r_head\n",
" line_head.append([x, 0., z, 1])\n",
"\n",
"line_foot = []\n",
"for phi_i in np.linspace(-phi_head, phi_head, 10):\n",
" x = r_w + y_p - np.cos(phi_i) * r_foot\n",
" z = np.sin(phi_i) * r_foot\n",
" line_foot.append([x, 0., z, 1])\n",
"\n",
"curves_head = []\n",
"curves_foot = []\n",
"phi_j_max = 2 * np.pi / teeth\n",
"for phi_j in np.linspace(-phi_j_max, phi_j_max, 10):\n",
" T = numeric_transformation(phi_j, np.array([0., 0., 1.]))\n",
" temp_points_foot = [app.Vector(*(T @ point)[:3]) for point in line_foot]\n",
" temp_points_head = [app.Vector(*(T @ point)[:3]) for point in line_head]\n",
" bsp_foot = part.BSplineCurve()\n",
" bsp_head = part.BSplineCurve()\n",
" bsp_foot.interpolate(temp_points_foot)\n",
" bsp_head.interpolate(temp_points_head)\n",
" curves_foot.append(bsp_foot.toShape())\n",
" curves_head.append(bsp_head.toShape())\n",
"\n",
"part.show(part.makeLoft(curves_foot))\n",
"part.show(part.makeLoft(curves_head))"
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "268c6302-4e7d-45e0-be28-1b64cf8b766e",
"execution_count": 21,
"id": "73769524-0356-4364-acbc-c4a28d26fc57",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\frac{\\sqrt{x^{2} + y^{2}}}{\\cos{\\left(\\alpha \\right)}}$"
],
"text/plain": [
"sqrt(x**2 + y**2)/cos(alpha)"
"name": "stdout",
"output_type": "stream",
"text": [
"t_start_1: -2.7474774194546225\n",
"t_start_0: 2.7474774194546225\n",
"t0: 0.7015390936864874, x0: 3.1224989948933315, min: 1.8775010319993481\n",
"t1: 0.7015390936864874, x1: 5.454356051426789, min: 0.47093369124192\n",
"t0: 0.5482381702072173, x0: 3.7996710369252895, min: 1.2005967588403554\n",
"t1: 0.5482381702072173, x1: 5.868347291705316, min: 0.8705739256491289\n",
"t0: -2.7474774194546225, x0: 4.0, min: 3.2681962153219666\n",
"t1: -2.7474774194546225, x1: 6.0, min: 3.2681962153219666\n",
"t0: -2.7218186874199746, x0: 3.7996710378205463, min: 3.1634473327759642\n",
"t1: -2.7218186874199746, x1: 5.868347293420843, min: 0.86835219843846\n",
"t0: 0.024534606456801573, x0: 3.1224989985781515, min: 1.877501936426569\n",
"t1: 0.024534606456801573, x1: 5.454356049869874, min: 0.45435622754073013\n"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y_cross_section = sp.simplify(sp.solve(spiral_x[1]- y, s)[0])\n",
"y_cross_section"
"import scipy as sp\n",
"import numpy as np\n",
"from freecad import part\n",
"from freecad import app\n",
"from pygears.transformation import numeric_transformation\n",
"\n",
"debug = False\n",
"def compute_involute(module=1, teeth=15, height=5, worm_pitch_diameter=10, num_threads=1, alpha=np.deg2rad(20)):\n",
" y = 0.\n",
" xw = worm_pitch_diameter / 2\n",
" \n",
" def length(x, y):\n",
" return (x**2 + y**2)**(0.5)\n",
" \n",
" def z(x, y, t):\n",
" r = length(x, y)\n",
" return module * num_threads * np.arcsin(y / r) / 2 + (r - xw) * np.tan(alpha) + t\n",
"\n",
" def distance_pw(x, y, t):\n",
" return np.sqrt((xw - x) ** 2 + z(x, y, t) ** 2)\n",
"\n",
" def min_root(x, y, t):\n",
" r0 = xw - module # * (1 + clearence)\n",
" x_t = sp.optimize.minimize(lambda x: distance_pw(x, y, t), (t)).x[0]\n",
" return distance_pw(x, y, t) + np.abs(r0**2 - x**2 - y**2)\n",
"\n",
" def min_head(x, y, t):\n",
" r1 = xw + module # * (1 + clearence)\n",
" return distance_pw(x, y, t) + np.abs(r1**2 - x**2 - y**2)\n",
" \n",
" xyz = [] \n",
" r0 = xw - module # * (1 + clearence)\n",
" r1 = xw + module # * (1 + clearence)\n",
" t_start_0 = (r0 - xw) / np.tan(alpha)\n",
" t_start_1 = (r1 - xw) / np.tan(alpha)\n",
"\n",
" \n",
" for y in np.linspace(-height / 2, height / 2, 20):\n",
" for t in np.linspace(t_start_0, t_start_1, 20):\n",
" x_t = sp.optimize.minimize(lambda x: distance_pw(x, y, t), xw).x[0]\n",
" z_t = z(x_t, y, t)\n",
" point = App.Vector(x_t, y, z_t)\n",
" part.show(part.Point(point).toShape())\n",
" \n",
" \n",
"compute_involute()"
]
},
{
"cell_type": "code",
"execution_count": 44,
"id": "7284bd6a-b47b-483c-8e9f-79fcaecce5e3",
"execution_count": 20,
"id": "26079daf-9e99-4365-89f4-c560e2aac9bd",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle \\left[\\begin{matrix}x\\\\\\left|{y}\\right|\\\\\\sqrt{x^{2} + y^{2}} \\tan{\\left(\\alpha \\right)} + \\pi \\operatorname{asin}{\\left(\\frac{x}{\\sqrt{x^{2} + y^{2}}} \\right)}\\\\1.0\\end{matrix}\\right]$"
],
"text/plain": [
"Matrix([\n",
"[ x],\n",
"[ Abs(y)],\n",
"[sqrt(x**2 + y**2)*tan(alpha) + pi*asin(x/sqrt(x**2 + y**2))],\n",
"[ 1.0]])"
"ename": "SyntaxError",
"evalue": "expected ':' (2870794076.py, line 1)",
"output_type": "error",
"traceback": [
"\u001b[0;36m Cell \u001b[0;32mIn[20], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m def debug_surface()\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m expected ':'\n"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spiral_xy = sp.simplify(spiral_x.subs({s: y_cross_section}))\n",
"spiral_xy"
" def debug_surface()\n",
" x_i = np.linspace(r_w / 10., r_w * 3. / 2., 10)\n",
" y_i = np.linspace(-r_w, r_w, 10)\n",
" \n",
" \n",
" # Gitterpunkte berechnen\n",
" xv, yv = np.meshgrid(x_i, y_i)\n",
" zv = z(xv, yv)\n",
" \n",
" # Punkte für die B-Spline-Fläche erzeugen\n",
" points = []\n",
" for i in range(xv.shape[0]):\n",
" row = []\n",
" for j in range(xv.shape[1]):\n",
" point = FreeCAD.Vector(xv[i, j], yv[i, j], zv[i, j])\n",
" # part.show(part.Point(point).toShape())\n",
" row.append(point)\n",
" points.append(row)\n",
" \n",
" # B-Spline-Fläche erstellen\n",
" bspline_surface = Part.BSplineSurface()\n",
" bspline_surface.approximate(points)\n",
" \n",
" # Fläche in FreeCAD-Dokument einfügen\n",
" doc = FreeCAD.ActiveDocument if FreeCAD.ActiveDocument else FreeCAD.newDocument()\n",
" bspline_shape = Part.Shape(bspline_surface)\n",
" part_object = doc.addObject(\"Part::Feature\", \"BSplineSurface\")\n",
" part_object.Shape = bspline_shape\n",
" doc.recompute()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "82e6880a-7240-44a4-a346-4fc45e24971b",
"id": "e0bfeffb-7261-4976-8eb3-805af31d4bfd",
"metadata": {},
"outputs": [],
"source": []
@@ -426,7 +786,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
"version": "3.13.1"
}
},
"nbformat": 4,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

19
freecad/gears/Init.py Normal file
View File

@@ -0,0 +1,19 @@
"""Kindred Gears - Console initialization.
Adds the gears repo root to sys.path so the pygears math library
and the freecad.gears namespace package are importable.
"""
import os
import sys
# mods/gears/freecad/gears/Init.py -> dirname x3 -> mods/gears/
_repo_root = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
if _repo_root not in sys.path:
sys.path.insert(0, _repo_root)
import FreeCAD
FreeCAD.Console.PrintLog("kindred-gears loaded\n")

40
freecad/gears/InitGui.py Normal file
View File

@@ -0,0 +1,40 @@
"""Kindred Gears - GUI initialization.
Imports the upstream GearWorkbench registration and injects gear
commands into PartDesign editing contexts via the Kindred SDK.
"""
from freecad.gears.init_gui import * # noqa: F401,F403
def _inject_gear_context():
"""Inject gear commands into PartDesign editing contexts."""
try:
from kindred_sdk import inject_commands
gear_commands = [
"FCGear_InvoluteGear",
"FCGear_InternalInvoluteGear",
"FCGear_InvoluteRack",
"FCGear_CycloidGear",
"FCGear_CycloidRack",
"FCGear_BevelGear",
"FCGear_CrownGear",
"FCGear_WormGear",
"FCGear_TimingGearT",
"FCGear_TimingGear",
"FCGear_LanternGear",
"FCGear_HypoCycloidGear",
"FCGear_GearConnector",
]
inject_commands("partdesign.body", "Gear", gear_commands)
inject_commands("partdesign.feature", "Gear", gear_commands)
except Exception as e:
import FreeCAD
FreeCAD.Console.PrintWarning(f"kindred-gears: context injection failed: {e}\n")
from PySide6.QtCore import QTimer
QTimer.singleShot(500, _inject_gear_context)

332
freecad/gears/basegear.py Normal file
View File

@@ -0,0 +1,332 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import os
import sys
import numpy as np
from freecad import app
from freecad import part
from pygears import __version__
from pygears._functions import arc_from_points_and_center
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
def fcvec(x):
"""tranforms a list or numpy array to a FreeCAD Vector which is
always 3d
Args:
x (iterable): either a 2d or 3d vector
Returns:
freecad.app.Vector: _description_
"""
if len(x) == 2:
return app.Vector(x[0], x[1], 0)
else:
return app.Vector(x[0], x[1], x[2])
class ViewProviderGear:
"""
The base Viewprovider for the gears
"""
def __init__(self, obj, icon_fn=None):
# Set this object to the proxy object of the actual view provider
obj.Proxy = self
self._check_attr()
dirname = os.path.dirname(__file__)
self.icon_fn = icon_fn or os.path.join(dirname, "icons", "involutegear.svg")
def _check_attr(self):
"""
Check for missing attributes.
"""
if not hasattr(self, "icon_fn"):
setattr(
self,
"icon_fn",
os.path.join(os.path.dirname(__file__), "icons", "involutegear.svg"),
)
def attach(self, vobj):
self.vobj = vobj
def getIcon(self):
self._check_attr()
return self.icon_fn
def dumps(self):
self._check_attr()
return {"icon_fn": self.icon_fn}
def loads(self, state):
if state and "icon_fn" in state:
self.icon_fn = state["icon_fn"]
def __getstate__(self):
self._check_attr()
return {"icon_fn": self.icon_fn}
def __setstate__(self, state):
if state and "icon_fn" in state:
self.icon_fn = state["icon_fn"]
class BaseGear:
def __init__(self, obj):
obj.addProperty(
"App::PropertyString",
"version",
"version",
QT_TRANSLATE_NOOP("App::Property", "freecad.gears-version"),
1,
)
obj.version = __version__
self.make_attachable(obj)
def make_attachable(self, obj):
"""
Needed to make this object "attachable",
aka able to attach parameterically to other objects
cf. https://wiki.freecadweb.org/Scripted_objects_with_attachment
"""
if int(app.Version()[0]) == 0 and int(app.Version()[1]) >= 19 or int(app.Version()[0]) == 1:
obj.addExtension("Part::AttachExtensionPython")
else:
obj.addExtension("Part::AttachExtensionPython", obj)
# unveil the "Placement" property, which seems hidden by default in PartDesign
obj.setEditorMode("Placement", 0) # non-readonly non-hidden
def execute(self, obj):
# checksbackwardcompatibility:
if not hasattr(obj, "positionBySupport"):
self.make_attachable(obj)
obj.positionBySupport()
# Backward compatibility for old files
if hasattr(obj, "teeth"):
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
"number of teeth",
).num_teeth = (15, 3, 10000, 1)
app.Console.PrintLog(
app.Qt.translate(
"Log", "Migrating 'teeth' property to 'num_teeth' on {} part\n"
).format(obj.Name)
)
obj.num_teeth = obj.teeth # Copy old value to new property
obj.removeProperty("teeth") # Remove the old property
gear_shape = self.generate_gear_shape(obj)
if hasattr(obj, "BaseFeature") and obj.BaseFeature != None:
# we're inside a PartDesign Body, thus need to fuse with the base feature
gear_shape.Placement = (
obj.Placement
) # ensure the gear is placed correctly before fusing
result_shape = obj.BaseFeature.Shape.fuse(gear_shape)
result_shape.transformShape(
obj.Placement.inverse().toMatrix(), True
) # account for setting obj.Shape below moves the shape to obj.Placement, ignoring its previous placement
obj.Shape = result_shape
else:
obj.Shape = gear_shape
def generate_gear_shape(self, obj):
"""
This method has to return the TopoShape of the gear.
"""
raise NotImplementedError("generate_gear_shape not implemented")
def loads(self, state):
pass
def dumps(self):
pass
def __setstate__(self, state):
pass
def __getstate__(self):
pass
def part_arc_from_points_and_center(point_1, point_2, center):
"""_summary_
Args:
point_1 (list, np.array with 2 values): 2d point start of arc
point_1 (list, np.array with 2 values): 2d point end of arc
center (list, np.array with 2 values): the 2d center of the arc
Returns:
freecad.part.Arc: a arc with
"""
p_1, p_12, p_2 = arc_from_points_and_center(point_1, point_2, center)
return part.Arc(fcvec(p_1), fcvec(p_12), fcvec(p_2))
def helical_extrusion(face, height, angle, double_helix=False):
"""
A helical extrusion using the BRepOffsetAPI
face -- the face to extrude (may contain holes, i.e. more then one wires)
height -- the height of the extrusion, normal to the face
angle -- the twist angle of the extrusion in radians
returns a solid
"""
pitch = height * 2 * np.pi / abs(angle)
radius = 10.0 # as we are only interested in the "twist", we take an arbitrary constant here
cone_angle = 0
direction = bool(angle < 0)
if double_helix:
spine = part.makeHelix(pitch, height / 2.0, radius, cone_angle, direction)
spine.translate(app.Vector(0, 0, height / 2.0))
face = face.translated(
app.Vector(0, 0, height / 2.0)
) # don't transform our argument
else:
spine = part.makeHelix(pitch, height, radius, cone_angle, direction)
def make_pipe(path, profile):
"""
returns (shell, last_wire)
"""
mkPS = part.BRepOffsetAPI.MakePipeShell(path)
mkPS.setFrenetMode(
True
) # otherwise, the profile's normal would follow the path
mkPS.add(profile, False, False)
mkPS.build()
return (mkPS.shape(), mkPS.lastShape())
shell_faces = []
top_wires = []
for wire in face.Wires:
pipe_shell, top_wire = make_pipe(spine, wire)
shell_faces.extend(pipe_shell.Faces)
top_wires.append(top_wire)
top_face = part.Face(top_wires)
shell_faces.append(top_face)
if double_helix:
origin = app.Vector(0, 0, height / 2.0)
xy_normal = app.Vector(0, 0, 1)
mirror_xy = lambda f: f.mirror(origin, xy_normal)
bottom_faces = list(map(mirror_xy, shell_faces))
shell_faces.extend(bottom_faces)
# TODO: why the heck is makeShell from this empty after mirroring?
# ... and why the heck does it work when making an intermediate compound???
hacky_intermediate_compound = part.makeCompound(shell_faces)
shell_faces = hacky_intermediate_compound.Faces
else:
shell_faces.append(face) # the bottom is what we extruded
shell = part.makeShell(shell_faces)
# shell.sewShape() # fill gaps that may result from accumulated tolerances. Needed?
# shell = shell.removeSplitter() # refine. Needed?
return part.makeSolid(shell)
def make_face(edge1, edge2):
v1, v2 = edge1.Vertexes
v3, v4 = edge2.Vertexes
e1 = part.Wire(edge1)
e2 = part.LineSegment(v1.Point, v3.Point).toShape().Edges[0]
e3 = edge2
e4 = part.LineSegment(v4.Point, v2.Point).toShape().Edges[0]
w = part.Wire([e3, e4, e1, e2])
return part.Face(w)
def make_bspline_wire(pts):
wi = []
for i in pts:
out = part.BSplineCurve()
out.interpolate(list(map(fcvec, i)))
wi.append(out.toShape())
return part.Wire(wi)
def points_to_wire(pts):
wire = []
for i in pts:
if len(i) == 2:
# straight edge
out = part.LineSegment(*list(map(fcvec, i)))
else:
out = part.BSplineCurve()
out.interpolate(list(map(fcvec, i)))
wire.append(out.toShape())
return part.Wire(wire)
def rotate_tooth(base_tooth, num_teeth):
rot = app.Matrix()
rot.rotateZ(2 * np.pi / num_teeth)
flat_shape = [base_tooth]
for t in range(num_teeth - 1):
flat_shape.append(flat_shape[-1].transformGeometry(rot))
return part.Wire(flat_shape)
def fillet_between_edges(edge_1, edge_2, radius, reversed=False):
# assuming edges are in a plane
# extracting vertices
fillet2d_api = part.ChFi2d.FilletAPI()
p1 = edge_1.valueAt(edge_1.FirstParameter)
p2 = edge_1.valueAt(edge_1.LastParameter)
p3 = edge_2.valueAt(edge_2.FirstParameter)
p4 = edge_2.valueAt(edge_2.LastParameter)
t1 = p2 - p1
t2 = p4 - p3
n = t1.cross(t2) * (-reversed * 2 + 1)
pln = part.Plane(edge_1.valueAt(edge_1.FirstParameter), n)
fillet2d_api.init(edge_1, edge_2, pln)
if fillet2d_api.perform(radius) > 0:
p0 = (p2 + p3) / 2
fillet, e1, e2 = fillet2d_api.result(p0)
return part.Wire([e1, fillet, e2]).Edges
else:
return None
def insert_fillet(edges, pos, radius, reversed=False):
assert pos < (len(edges) - 1)
e1 = edges[pos]
e2 = edges[pos + 1]
if radius > 0:
fillet_edges = fillet_between_edges(e1, e2, radius, reversed)
if not fillet_edges:
raise RuntimeError("fillet not possible")
else:
fillet_edges = [e1, None, e2]
output_edges = []
for i, edge in enumerate(edges):
if i == pos:
output_edges += fillet_edges
elif i == (pos + 1):
pass
else:
output_edges.append(edge)
return output_edges

259
freecad/gears/bevelgear.py Normal file
View File

@@ -0,0 +1,259 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
from freecad import app
from freecad import part
import numpy as np
from pygears.bevel_tooth import BevelTooth
from pygears._functions import rotation3D
from .basegear import BaseGear, fcvec, make_bspline_wire
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class BevelGear(BaseGear):
"""parameters:
pressure_angle: pressureangle, 10-30°
pitch_angle: cone angle, 0 < pitch_angle < pi/4
"""
def __init__(self, obj):
super(BevelGear, self).__init__(obj)
self.bevel_tooth = BevelTooth()
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyAngle",
"pitch_angle",
"involute",
QT_TRANSLATE_NOOP("App::Property", "pitch_angle"),
)
obj.addProperty(
"App::PropertyAngle",
"pressure_angle",
"involute_parameter",
QT_TRANSLATE_NOOP("App::Property", "pressure_angle"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP("App::Property", "module"),
)
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP("App::Property", "clearance"),
)
obj.addProperty(
"App::PropertyInteger",
"numpoints",
"precision",
QT_TRANSLATE_NOOP("App::Property", "number of points for spline"),
)
obj.addProperty(
"App::PropertyBool",
"reset_origin",
"base",
QT_TRANSLATE_NOOP(
"App::Property",
"if value is true the gears outer face will match the z=0 plane",
),
)
obj.addProperty(
"App::PropertyLength",
"backlash",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property",
"The arc length on the pitch circle by which the tooth thickness is reduced.",
),
)
obj.addProperty(
"App::PropertyPythonObject",
"gear",
"base",
QT_TRANSLATE_NOOP("App::Property", "test"),
)
obj.addProperty(
"App::PropertyAngle",
"beta",
"helical",
QT_TRANSLATE_NOOP("App::Property", "angle used for spiral bevel-gears"),
)
obj.addProperty(
"App::PropertyLength",
"dw",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
)
obj.setExpression(
"dw", "num_teeth * module"
) # calculate via expression to ease usage for placement
obj.setEditorMode(
"dw", 1
) # set read-only after setting the expression, else it won't be visible. bug?
obj.addProperty(
"App::PropertyAngle",
"angular_backlash",
"computed",
QT_TRANSLATE_NOOP(
"App::Property",
"The angle by which this gear can turn without moving the mating gear.",
),
)
obj.setExpression(
"angular_backlash", "backlash / dw * 360° / pi"
) # calculate via expression to ease usage for placement
obj.setEditorMode(
"angular_backlash", 1
) # set read-only after setting the expression, else it won't be visible. bug?
obj.gear = self.bevel_tooth
obj.module = "1. mm"
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.pressure_angle = "20. deg"
obj.pitch_angle = "45. deg"
obj.height = "5. mm"
obj.numpoints = 20
obj.backlash = "0.00 mm"
obj.clearance = 0.1
obj.beta = "0 deg"
obj.reset_origin = True
self.obj = obj
obj.Proxy = self
def generate_gear_shape(self, fp):
fp.gear.z = fp.num_teeth
fp.gear.module = fp.module.Value
fp.gear.pressure_angle = (90 - fp.pressure_angle.Value) * np.pi / 180.0
fp.gear.pitch_angle = fp.pitch_angle.Value * np.pi / 180
max_height = fp.gear.module * fp.num_teeth / 2 / np.tan(fp.gear.pitch_angle)
if fp.height >= max_height:
app.Console.PrintWarning(
"height must be smaller than {}".format(max_height)
)
fp.gear.backlash = fp.backlash.Value
scale = (
fp.module.Value
* fp.num_teeth
/ 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.num_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.num_teeth - 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 = []
if not "version" in fp.PropertiesList:
scale_0 = scale - fp.height.Value / 2
scale_1 = scale + fp.height.Value / 2
else: # starting with version 0.0.2
scale_0 = scale - fp.height.Value
scale_1 = scale
if fp.beta.Value == 0:
wires.append(make_bspline_wire([scale_0 * p for p in pts]))
wires.append(make_bspline_wire([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.0
* np.sin(np.pi / 4)
/ np.sin(fp.pitch_angle.Value * np.pi / 180.0)
)
points = [
np.array([self.spherical_rot(p, angle) for p in scale_i * pt])
for pt in pts
]
wires.append(make_bspline_wire(points))
shape = part.makeLoft(wires, True)
if fp.reset_origin:
mat = app.Matrix()
mat.A33 = -1
mat.move(fcvec([0, 0, scale_1]))
shape = shape.transformGeometry(mat)
return shape
# return self.create_teeth(pts, pos1, fp.num_teeth)
def create_tooth(self):
w = []
scal1 = (
self.obj.m.Value
* self.obj.num_teeth
/ 2
/ np.tan(self.obj.pitch_angle.Value * np.pi / 180)
- self.obj.height.Value / 2
)
scal2 = (
self.obj.m.Value
* self.obj.num_teeth
/ 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 = []
def scale(x):
return 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 = part.BSplineSurface()
b.interpolate(i)
surfs.append(b)
return part.Shape(surfs)
def spherical_rot(self, point, phi):
new_phi = np.sqrt(np.linalg.norm(point)) * phi
return rotation3D(-new_phi)(point)

View File

@@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * This file defines the ChainConnector class for stable multi-gear chains. *
# * *
# * (GNU GPL Header retained)
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from pygears import __version__ # FIX: Import __version__
# Import all gear types the chain might connect
from .involutegear import InvoluteGear
from .internalinvolutegear import InternalInvoluteGear
from .cycloidgear import CycloidGear
# Reuse the standard ViewProvider from the original connector.py
from .connector import ViewProviderGearConnector, GearConnector
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class Chain(object):
def __init__(self, obj, gear_list):
obj.addProperty("App::PropertyLinkList", "gear_list", "GearChain", "list of gears in the chain")
obj.addProperty("App::PropertyAngle", "angle", "GearChain", "angle of the first gear")
obj.gear_list = gear_list
obj.Proxy = self
self.obj = obj
ViewProviderGearConnector(obj.ViewObject)
# Create connectors
master_gear = gear_list[0]
for i, slave_gear in enumerate(gear_list[1:]):
if i == 0:
connector = app.ActiveDocument.addObject("Part::FeaturePython", "GearConnector")
GearConnector(connector, master_gear, slave_gear)
connector.angle1 = self.obj.angle
self.obj.addProperty("App::PropertyLink", "connector", "GearChain", "main connector")
self.obj.connector = connector
else:
connector = app.ActiveDocument.addObject("Part::FeaturePython", "ChainConnector")
ChainConnector(connector, self.obj.connector, slave_gear)
master_gear = slave_gear
def onChanged(self, fp, prop):
if prop == 'angle':
fp.connector.angle1 = fp.angle
class ChainConnector(object):
def __init__(self, obj, master_connector, slave_gear):
# --- PROPERTY DEFINITIONS ---
obj.addProperty("App::PropertyString", "version", "version",
QT_TRANSLATE_NOOP("App::Property", "freecad.gears-version"), 1)
obj.addProperty("App::PropertyLink", "input_connector", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "Previous connector in the chain"), 1)
obj.addProperty("App::PropertyLink", "master_gear", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "The shared master gear (G2)"), 8) # Read-only link to G2
obj.addProperty("App::PropertyLink", "slave_gear", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "The new slave gear (G3)"), 1)
obj.addProperty("App::PropertyAngle", "input_gear_angle", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "Calculated rotation angle of the shared gear (G2)"), 8)
# FIX 2: Add version property assignment
obj.version = __version__
obj.input_connector = master_connector
obj.slave_gear = slave_gear
obj.Proxy = self
# CRITICAL FIX: Attach the ViewProvider to ensure visibility
ViewProviderGearConnector(obj.ViewObject)
# 1. Link master_gear (G2) property to the slave_gear of the previous connector (GC1.slave_gear)
obj.setExpression('master_gear', f'{master_connector.Name}.slave_gear')
# 2. Link the input_gear_angle to G2's rotation. This triggers onChanged whenever G2 moves.
# We convert radians (from .Angle) to degrees for consistency, though
# onChanged will convert it back. Or we can just use the raw Angle.
obj.setExpression('input_gear_angle', f'{master_connector.Name}.slave_gear.Placement.Rotation.Angle')
self.onChanged(obj, 'input_gear_angle')
def __init__(self, obj, master_connector, slave_gear):
# --- PROPERTY DEFINITIONS ---
obj.addProperty("App::PropertyString", "version", "version",
QT_TRANSLATE_NOOP("App::Property", "freecad.gears-version"), 1)
obj.addProperty("App::PropertyLink", "input_connector", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "Previous connector in the chain"), 1)
obj.addProperty("App::PropertyLink", "master_gear", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "The shared master gear (G2)"), 8) # Read-only link to G2
obj.addProperty("App::PropertyLink", "slave_gear", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "The new slave gear (G3)"), 1)
obj.addProperty("App::PropertyAngle", "input_gear_angle", "GearChain",
QT_TRANSLATE_NOOP("App::Property", "Calculated rotation angle of the shared gear (G2)"), 8)
# FIX 2: Add version property assignment
obj.version = __version__
obj.input_connector = master_connector
obj.slave_gear = slave_gear
obj.Proxy = self
# CRITICAL FIX: Attach the ViewProvider to ensure visibility
ViewProviderGearConnector(obj.ViewObject)
# 1. Link master_gear (G2) property to the slave_gear of the previous connector (GC1.slave_gear)
obj.setExpression('master_gear', f'{master_connector.Name}.slave_gear')
# 2. Link the input_gear_angle to G2's rotation. This triggers onChanged whenever G2 moves.
# We convert radians (from .Angle) to degrees for consistency, though
# onChanged will convert it back. Or we can just use the raw Angle.
obj.setExpression('input_gear_angle', f'{master_connector.Name}.slave_gear.Placement.Rotation.Angle')
def onChanged(self, fp, prop):
# Only react when the input angle (G2's rotation) or the final slave gear (G3) link changes
if prop not in ('input_gear_angle', 'slave_gear'):
return
# Ensure we have both gears before proceeding
if fp.master_gear is None or fp.slave_gear is None:
return
# input_gear_angle is linked to Placement.Rotation.Angle, which is in RADIANS
master_angle_rad = fp.input_gear_angle.Value
master_angle_deg = np.rad2deg(master_angle_rad) # Convert to degrees
dw_master = fp.master_gear.pitch_diameter.Value
dw_slave = fp.slave_gear.pitch_diameter.Value
# --- Involute Gear Pair Logic ---
if isinstance(fp.master_gear.Proxy, InvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGear):
dist = (dw_master + dw_slave) / 2
slave_pos = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
# Kinematics: Calculate slave rotation (opposite direction)
angle_slave_deg = -(dw_master / dw_slave) * master_angle_deg
# Apply rotation and position to G3 (slave_gear)
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot_slave = app.Rotation(app.Vector(0, 0, 1), angle_slave_deg + angle3)
fp.slave_gear.Placement = app.Placement(slave_pos, rot_slave)
fp.slave_gear.purgeTouched()
# --- Internal Involute Gear Logic ---
elif isinstance(fp.master_gear.Proxy, InternalInvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGear):
dist = (dw_master - dw_slave) / 2
slave_pos = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
# Kinematics: Calculate slave rotation (same direction)
angle_slave_deg = (dw_master / dw_slave) * master_angle_deg
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot_slave = app.Rotation(app.Vector(0, 0, 1), angle_slave_deg + angle3)
fp.slave_gear.Placement = app.Placement(slave_pos, rot_slave)
fp.slave_gear.purgeTouched()
# --- Cycloid Gear Pair Logic ---
elif isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance(fp.slave_gear.Proxy, CycloidGear):
dist = (dw_master + dw_slave) / 2
slave_pos = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
# Kinematics: Calculate slave rotation (opposite direction)
angle_slave_deg = -(dw_master / dw_slave) * master_angle_deg
# Apply rotation and position to G3 (slave_gear)
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot_slave = app.Rotation(app.Vector(0, 0, 1), angle_slave_deg + angle3)
fp.slave_gear.Placement = app.Placement(slave_pos, rot_slave)
fp.slave_gear.purgeTouched()
# [Add Rack Logic here if chains need to drive racks]
def execute(self, fp):
# When executed, simply trigger the onChanged to use the current expression-linked value
self.onChanged(fp, 'input_gear_angle')

View File

@@ -17,27 +17,30 @@
# ***************************************************************************
import os
import FreeCAD
import FreeCADGui as Gui
from freecad import app
from freecad import gui
from .features import (
ViewProviderGear,
InvoluteGear,
InternalInvoluteGear,
InvoluteGearRack,
CycloidGearRack,
CycloidGear,
BevelGear,
CrownGear,
WormGear,
TimingGear,
LanternGear,
HypoCycloidGear,
BaseGear,
)
from .timing_gear_t import TimingGearT
from .basegear import ViewProviderGear, BaseGear
from .timinggear_t import TimingGearT
from .involutegear import InvoluteGear
from .internalinvolutegear import InternalInvoluteGear
from .involutegearrack import InvoluteGearRack
from .cycloidgearrack import CycloidGearRack
from .crowngear import CrownGear
from .cycloidgear import CycloidGear
from .bevelgear import BevelGear
from .wormgear import WormGear
from .timinggear import TimingGear
from .lanterngear import LanternGear
from .hypocycloidgear import HypoCycloidGear
# CRITICAL CHANGE: Import both connector types
from .connector import GearConnector, ViewProviderGearConnector
from .chainconnector import ChainConnector, Chain
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class BaseCommand(object):
@@ -49,33 +52,33 @@ class BaseCommand(object):
pass
def IsActive(self):
if FreeCAD.ActiveDocument is None:
if app.ActiveDocument is None:
return False
else:
return True
def Activated(self):
Gui.doCommandGui("import freecad.gears.commands")
Gui.doCommandGui(
gui.doCommandGui("import freecad.gears.commands")
gui.doCommandGui(
"freecad.gears.commands.{}.create()".format(self.__class__.__name__)
)
FreeCAD.ActiveDocument.recompute()
Gui.SendMsgToActiveView("ViewFit")
app.ActiveDocument.recompute()
gui.SendMsgToActiveView("ViewFit")
@classmethod
def create(cls):
if FreeCAD.GuiUp:
if app.GuiUp:
# borrowed from threaded profiles
# puts the gear into an active container
body = Gui.ActiveDocument.ActiveView.getActiveObject("pdbody")
part = Gui.ActiveDocument.ActiveView.getActiveObject("part")
body = gui.ActiveDocument.ActiveView.getActiveObject("pdbody")
part = gui.ActiveDocument.ActiveView.getActiveObject("part")
if body:
obj = FreeCAD.ActiveDocument.addObject(
obj = app.ActiveDocument.addObject(
"PartDesign::FeaturePython", cls.NAME
)
else:
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", cls.NAME)
obj = app.ActiveDocument.addObject("Part::FeaturePython", cls.NAME)
ViewProviderGear(obj.ViewObject, cls.Pixmap)
cls.GEAR_FUNCTION(obj)
@@ -84,7 +87,7 @@ class BaseCommand(object):
elif part:
part.Group += [obj]
else:
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", cls.NAME)
obj = app.ActiveDocument.addObject("Part::FeaturePython", cls.NAME)
cls.GEAR_FUNCTION(obj)
return obj
@@ -100,117 +103,164 @@ class CreateInvoluteGear(BaseCommand):
NAME = "InvoluteGear"
GEAR_FUNCTION = InvoluteGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "involutegear.svg")
MenuText = "Involute Gear"
ToolTip = "Create an external involute gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_InvoluteGear", "Involute Gear")
ToolTip = QT_TRANSLATE_NOOP(
"FCGear_InvoluteGear", "Create an external involute gear"
)
class CreateInternalInvoluteGear(BaseCommand):
NAME = "InternalInvoluteGear"
GEAR_FUNCTION = InternalInvoluteGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "internalinvolutegear.svg")
MenuText = "Internal Involute Gear"
ToolTip = "Create an internal involute gear"
MenuText = QT_TRANSLATE_NOOP(
"FCGear_InternalInvoluteGear", "Internal Involute Gear"
)
ToolTip = QT_TRANSLATE_NOOP(
"FCGear_InternalInvoluteGear", "Create an internal involute gear"
)
class CreateInvoluteRack(BaseCommand):
NAME = "InvoluteRack"
GEAR_FUNCTION = InvoluteGearRack
Pixmap = os.path.join(BaseCommand.ICONDIR, "involuterack.svg")
MenuText = "Involute Rack"
ToolTip = "Create an Involute rack"
MenuText = QT_TRANSLATE_NOOP("FCGear_InvoluteRack", "Involute Rack")
ToolTip = QT_TRANSLATE_NOOP("FCGear_InvoluteRack", "Create an Involute rack")
class CreateCycloidRack(BaseCommand):
NAME = "CycloidRack"
GEAR_FUNCTION = CycloidGearRack
Pixmap = os.path.join(BaseCommand.ICONDIR, "cycloidrack.svg")
MenuText = "Cycloid Rack"
ToolTip = "Create an Cycloid rack"
MenuText = QT_TRANSLATE_NOOP("FCGear_CycloidRack", "Cycloid Rack")
ToolTip = QT_TRANSLATE_NOOP("FCGear_CycloidRack", "Create an Cycloid rack")
class CreateCrownGear(BaseCommand):
NAME = "CrownGear"
GEAR_FUNCTION = CrownGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "crowngear.svg")
MenuText = "Crown Gear"
ToolTip = "Create a Crown gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_CrownGear", "Crown Gear")
ToolTip = QT_TRANSLATE_NOOP("FCGear_CrownGear", "Create a Crown gear")
class CreateCycloidGear(BaseCommand):
NAME = "CycloidGear"
GEAR_FUNCTION = CycloidGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "cycloidgear.svg")
MenuText = "Cycloid Gear"
ToolTip = "Create a Cycloid gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_CycloidGear", "Cycloid Gear")
ToolTip = QT_TRANSLATE_NOOP("FCGear_CycloidGear", "Create a Cycloid gear")
class CreateBevelGear(BaseCommand):
NAME = "BevelGear"
GEAR_FUNCTION = BevelGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "bevelgear.svg")
MenuText = "Bevel Gear"
ToolTip = "Create a Bevel gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_BevelGear", "Bevel Gear")
ToolTip = QT_TRANSLATE_NOOP("FCGear_BevelGear", "Create a Bevel gear")
class CreateHypoCycloidGear(BaseCommand):
NAME = "HypocycloidGear"
GEAR_FUNCTION = HypoCycloidGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "hypocycloidgear.svg")
MenuText = "HypoCycloid Gear"
ToolTip = "Create a HypoCycloid gear with its pins"
MenuText = QT_TRANSLATE_NOOP("FCGear_HypoCycloidGear", "HypoCycloid Gear")
ToolTip = QT_TRANSLATE_NOOP(
"FCGear_HypoCycloidGear", "Create a HypoCycloid gear with its pins"
)
class CreateWormGear(BaseCommand):
NAME = "WormGear"
GEAR_FUNCTION = WormGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "wormgear.svg")
MenuText = "Worm Gear"
ToolTip = "Create a Worm gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_WormGear", "Worm Gear")
ToolTip = QT_TRANSLATE_NOOP("FCGear_WormGear", "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"
MenuText = QT_TRANSLATE_NOOP("FCGear_TimingGearT", "Timing Gear T-shape")
ToolTip = QT_TRANSLATE_NOOP("FCGear_TimingGearT", "Create a Timing gear T-shape")
class CreateTimingGear(BaseCommand):
NAME = "TimingGear"
GEAR_FUNCTION = TimingGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "timinggear.svg")
MenuText = "Timing Gear"
ToolTip = "Create a Timing gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_TimingGear", "Timing Gear")
ToolTip = QT_TRANSLATE_NOOP("FCGear_TimingGear", "Create a Timing gear")
class CreateLanternGear(BaseCommand):
NAME = "LanternGear"
GEAR_FUNCTION = LanternGear
Pixmap = os.path.join(BaseCommand.ICONDIR, "lanterngear.svg")
MenuText = "Lantern Gear"
ToolTip = "Create a Lantern gear"
MenuText = QT_TRANSLATE_NOOP("FCGear_LanternGear", "Lantern Gear")
ToolTip = QT_TRANSLATE_NOOP("FCGear_LanternGear", "Create a Lantern gear")
class CreateGearConnector(BaseCommand):
NAME = "GearConnector"
GEAR_FUNCTION = GearConnector
Pixmap = os.path.join(BaseCommand.ICONDIR, "gearconnector.svg")
MenuText = "Combine two gears"
ToolTip = "Combine two gears"
MenuText = QT_TRANSLATE_NOOP("FCGear_GearConnector", "Combine two gears")
ToolTip = QT_TRANSLATE_NOOP("FCGear_GearConnector", "Combine two gears")
def Activated(self):
gear1 = Gui.Selection.getSelection()[0]
assert isinstance(gear1.Proxy, BaseGear)
try:
selection = gui.Selection.getSelection()
gear2 = Gui.Selection.getSelection()[1]
assert isinstance(gear2.Proxy, BaseGear)
if len(selection) != 2:
raise ValueError(
app.Qt.translate("Log", "Please select two objects (gear+gear or connector+gear).")
)
# check if selected objects are beams
# Get the proxy types for the two selected objects
selection0_proxy = selection[0].Proxy if hasattr(selection[0], 'Proxy') else None
selection1_proxy = selection[1].Proxy if hasattr(selection[1], 'Proxy') else None
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", self.NAME)
GearConnector(obj, gear1, gear2)
# Identify the parent connector and the new slave gear
parent_connector = None
slave_gear = None
# Case 1: Connector (GC1) selected first, Gear (G3) second
if isinstance(selection0_proxy, GearConnector) and isinstance(selection1_proxy, BaseGear):
parent_connector = selection[0]
slave_gear = selection[1]
# Case 2: Gear (G3) selected first, Connector (GC1) second
elif isinstance(selection1_proxy, GearConnector) and isinstance(selection0_proxy, BaseGear):
parent_connector = selection[1]
slave_gear = selection[0]
# --- CRITICAL DECISION POINT ---
if parent_connector is not None:
# Import the ChainConnector class (we already imported it at the top)
# Chain Creation: Create the dedicated ChainConnector
obj = app.ActiveDocument.addObject("Part::FeaturePython", "ChainConnector")
ChainConnector(obj, parent_connector, slave_gear)
ViewProviderGearConnector(obj.ViewObject)
FreeCAD.ActiveDocument.recompute()
else:
# Standard Creation: Two gears selected (G1 and G2)
for obj_sel in selection:
if not isinstance(obj_sel.Proxy, BaseGear):
raise TypeError(
app.Qt.translate("Log", "Selected objects must be gears.")
)
obj = app.ActiveDocument.addObject("Part::FeaturePython", self.NAME)
GearConnector(obj, selection[0], selection[1])
ViewProviderGearConnector(obj.ViewObject)
app.ActiveDocument.recompute()
return obj
except Exception as e:
app.Console.PrintError(f"Error: {str(e)}\n")
return None

View File

@@ -17,18 +17,22 @@
# ***************************************************************************
import os
import sys
import numpy as np
import FreeCAD
from freecad import app
from pygears import __version__
from .features import (
InvoluteGear,
CycloidGear,
InvoluteGearRack,
CycloidGearRack,
InternalInvoluteGear,
)
from pygears.computation import compute_shifted_gears
from .involutegear import InvoluteGear
from .internalinvolutegear import InternalInvoluteGear
from .involutegearrack import InvoluteGearRack
from .cycloidgear import CycloidGear
from .cycloidgearrack import CycloidGearRack
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class ViewProviderGearConnector(object):
def __init__(self, vobj, icon_fn=None):
@@ -43,6 +47,15 @@ class ViewProviderGearConnector(object):
def getIcon(self):
return self.icon_fn
if sys.version_info[0] == 3 and sys.version_info[1] >= 11:
def dumps(self):
return {"icon_fn": self.icon_fn}
def loads(self, state):
self.icon_fn = state["icon_fn"]
else:
def __getstate__(self):
return {"icon_fn": self.icon_fn}
@@ -51,100 +64,234 @@ class ViewProviderGearConnector(object):
class GearConnector(object):
_recomputing = False
def __init__(self, obj, master_gear, slave_gear):
obj.addProperty(
"App::PropertyString", "version", "version", "freecad.gears-version", 1
"App::PropertyString",
"version",
"version",
QT_TRANSLATE_NOOP("App::Property", "freecad.gears-version"),
1,
)
obj.addProperty(
"App::PropertyLink",
"master_gear",
"gear",
QT_TRANSLATE_NOOP("App::Property", "master gear"),
1,
)
obj.addProperty(
"App::PropertyLink",
"slave_gear",
"gear",
QT_TRANSLATE_NOOP("App::Property", "slave gear"),
1,
)
obj.addProperty("App::PropertyLink", "master_gear", "gear", "master gear", 1)
obj.addProperty("App::PropertyLink", "slave_gear", "gear", "slave gear", 1)
obj.addProperty(
"App::PropertyAngle",
"angle1",
"gear",
"angle at which second gear is placed",
QT_TRANSLATE_NOOP("App::Property", "angle at which second gear is placed"),
0,
)
obj.addProperty(
"App::PropertyAngle",
"angle2",
"gear",
"angle at which second gear is placed",
QT_TRANSLATE_NOOP("App::Property", "angle at which second gear is placed"),
1,
)
obj.addProperty(
"App::PropertyBool",
"master_gear_stationary",
"gear",
QT_TRANSLATE_NOOP("App::Property", "master gear position is fixed (does not orbit)"),
0,
)
obj.addProperty(
"App::PropertyBool",
"slave_gear_stationary",
"gear",
QT_TRANSLATE_NOOP("App::Property", "slave gear position is fixed (does not orbit)"),
0,
)
obj.version = __version__
obj.master_gear = master_gear
obj.slave_gear = slave_gear
obj.angle1 = 0
obj.angle2 = 0
obj.master_gear_stationary = True
obj.slave_gear_stationary = True
obj.Proxy = self
# FIX 1: Attach ViewProvider to ensure visibility (fixes grey-out)
ViewProviderGearConnector(obj.ViewObject)
def onChanged(self, fp, prop):
# fp.angle2 = fp.master_gear.Placement.Rotation.Angle
if self._recomputing:
return
# Guard: Check if gears are initialized
if not hasattr(fp, 'master_gear') or not hasattr(fp, 'slave_gear'):
return
if fp.master_gear is None or fp.slave_gear is None:
return
# FIX 3: This connector is *always* driven by its angle1 property.
# Removing the 'if prop == angle1' check ensures it runs on
# manual changes (prop='angle1') AND on document recompute (prop=None).
# This provides a stable position for G2, which fixes the chain.
master_angle = fp.angle1.Value # Angle in degrees
# ====================================================================
# INVOLUTE GEAR TO INVOLUTE GEAR
# ====================================================================
if isinstance(fp.master_gear.Proxy, InvoluteGear) and isinstance(
fp.slave_gear.Proxy, InvoluteGear
):
angle_master = fp.master_gear.Placement.Rotation.Angle * sum(
fp.master_gear.Placement.Rotation.Axis
)
dw_master = fp.master_gear.dw
dw_slave = fp.slave_gear.dw
dw_master = fp.master_gear.pitch_diameter.Value
dw_slave = fp.slave_gear.pitch_diameter.Value
dist = (dw_master + dw_slave) / 2
if fp.master_gear.shift != 0 or fp.slave_gear.shift != 0:
dist, alpha_w = compute_shifted_gears(
fp.master_gear.module,
np.deg2rad(fp.master_gear.pressure_angle.Value),
fp.master_gear.teeth,
fp.slave_gear.teeth,
fp.master_gear.num_teeth,
fp.slave_gear.num_teeth,
fp.master_gear.shift,
fp.slave_gear.shift,
)
mat0 = FreeCAD.Matrix() # unity matrix
trans = FreeCAD.Vector(dist)
mat0.move(trans)
rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix()
angle2 = dw_master / dw_slave * fp.angle1.Value
angle4 = dw_master / dw_slave * np.rad2deg(angle_master)
rot2 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle2).toMatrix()
angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180.0 / fp.slave_gear.teeth
rot3 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle3).toMatrix()
rot4 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -angle4).toMatrix()
mat1 = rot * mat0 * rot2 * rot3 * rot4
mat1.move(fp.master_gear.Placement.Base)
fp.slave_gear.Placement = mat1
# Check if we have the stationary properties (for backward compatibility)
master_stationary = getattr(fp, 'master_gear_stationary', True)
slave_stationary = getattr(fp, 'slave_gear_stationary', False)
if master_stationary and slave_stationary:
# Both gears stay at their positions, only rotate in place
slave_position = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
# 1. Rotate G1 (master)
rot_master = app.Rotation(app.Vector(0, 0, 1), master_angle)
fp.master_gear.Placement = app.Placement(fp.master_gear.Placement.Base, rot_master)
fp.master_gear.purgeTouched()
# 2. Calculate G2 (slave) rotation
angle_slave = dw_master / dw_slave * master_angle
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot_slave = app.Rotation(app.Vector(0, 0, 1), -angle_slave + angle3)
# 3. Set G2's placement. This triggers the ChainConnector via Expression link.
fp.slave_gear.Placement = app.Placement(slave_position, rot_slave)
fp.slave_gear.purgeTouched()
elif master_stationary and not slave_stationary:
# Original behavior: slave gear orbits around master
orbit_angle = fp.angle1.Value
slave_rotation_angle = - (dw_master / dw_slave) * orbit_angle
# Slave gear's local rotation
slave_local_rot = app.Rotation(app.Vector(0, 0, 1), slave_rotation_angle)
# Orbital placement
orbit_rot = app.Rotation(app.Vector(0, 0, 1), orbit_angle)
orbit_pos = fp.master_gear.Placement.Base + orbit_rot.multVec(app.Vector(dist, 0, 0))
# Combine orbital placement with local rotation
fp.slave_gear.Placement = app.Placement(orbit_pos, orbit_rot * slave_local_rot)
fp.slave_gear.purgeTouched()
elif not master_stationary and slave_stationary:
# Master orbits around slave (inverse behavior)
orbit_angle = -fp.angle1.Value
master_rotation_angle = - (dw_slave / dw_master) * orbit_angle
# Master gear's local rotation
master_local_rot = app.Rotation(app.Vector(0, 0, 1), master_rotation_angle)
# Orbital placement
orbit_rot = app.Rotation(app.Vector(0, 0, 1), orbit_angle)
orbit_pos = fp.slave_gear.Placement.Base + orbit_rot.multVec(app.Vector(dist, 0, 0))
# Combine orbital placement with local rotation
fp.master_gear.Placement = app.Placement(orbit_pos, orbit_rot * master_local_rot)
fp.master_gear.purgeTouched()
# else: both not stationary - no action needed
if isinstance(fp.master_gear.Proxy, InternalInvoluteGear) and isinstance(
fp.slave_gear.Proxy, InvoluteGear
):
# Internal gear logic remains unchanged
angle_master = fp.master_gear.Placement.Rotation.Angle * sum(
fp.master_gear.Placement.Rotation.Axis
)
dw_master = fp.master_gear.dw
dw_slave = fp.slave_gear.dw
dw_master = fp.master_gear.pitch_diameter.Value
dw_slave = fp.slave_gear.pitch_diameter.Value
dist = (dw_master - dw_slave) / 2
if fp.master_gear.shift != 0 or fp.slave_gear.shift != 0:
dist, alpha_w = compute_shifted_gears(
fp.master_gear.module,
np.deg2rad(fp.master_gear.pressure_angle.Value),
fp.master_gear.teeth,
fp.slave_gear.teeth,
fp.master_gear.num_teeth,
fp.slave_gear.num_teeth,
fp.master_gear.shift,
fp.slave_gear.shift,
)
mat0 = FreeCAD.Matrix() # unity matrix
trans = FreeCAD.Vector(dist)
master_stationary = getattr(fp, 'master_gear_stationary', True)
slave_stationary = getattr(fp, 'slave_gear_stationary', False)
if master_stationary and slave_stationary:
slave_position = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
# Master rotates by angle1
rot_master = app.Rotation(app.Vector(0, 0, 1), fp.angle1.Value)
fp.master_gear.Placement = app.Placement(fp.master_gear.Placement.Base, rot_master)
fp.master_gear.purgeTouched()
# Slave gets positioned at correct distance and rotates based on gear ratio
angle_slave = -dw_master / dw_slave * fp.angle1.Value
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot_slave = app.Rotation(app.Vector(0, 0, 1), -angle_slave + angle3)
fp.slave_gear.Placement = app.Placement(slave_position, rot_slave)
fp.slave_gear.purgeTouched()
elif master_stationary and not slave_stationary:
mat0 = app.Matrix() # unity matrix
trans = app.Vector(dist)
mat0.move(trans)
rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix()
rot = app.Rotation(app.Vector(0, 0, 1), fp.angle1).toMatrix()
angle2 = -dw_master / dw_slave * fp.angle1.Value
angle4 = -dw_master / dw_slave * np.rad2deg(angle_master)
rot2 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle2).toMatrix()
angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180.0 / fp.slave_gear.teeth
rot3 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle3).toMatrix()
rot4 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -angle4).toMatrix()
rot2 = app.Rotation(app.Vector(0, 0, 1), angle2).toMatrix()
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot3 = app.Rotation(app.Vector(0, 0, 1), angle3).toMatrix()
rot4 = app.Rotation(app.Vector(0, 0, 1), -angle4).toMatrix()
mat1 = rot * mat0 * rot2 * rot3 * rot4
mat1.move(fp.master_gear.Placement.Base)
fp.slave_gear.Placement = mat1
fp.slave_gear.purgeTouched()
elif not master_stationary and slave_stationary:
mat0 = app.Matrix() # unity matrix
trans = app.Vector(dist)
mat0.move(trans)
rot = app.Rotation(app.Vector(0, 0, 1), -fp.angle1).toMatrix()
angle2 = -dw_slave / dw_master * fp.angle1.Value
angle_slave = fp.slave_gear.Placement.Rotation.Angle * sum(
fp.slave_gear.Placement.Rotation.Axis
)
angle4 = -dw_slave / dw_master * np.rad2deg(angle_slave)
rot2 = app.Rotation(app.Vector(0, 0, 1), angle2).toMatrix()
rot4 = app.Rotation(app.Vector(0, 0, 1), -angle4).toMatrix()
mat1 = rot * mat0 * rot2 * rot4
mat1.move(fp.slave_gear.Placement.Base)
fp.master_gear.Placement = mat1
fp.master_gear.purgeTouched()
if (
isinstance(fp.master_gear.Proxy, InvoluteGear)
@@ -153,47 +300,92 @@ class GearConnector(object):
isinstance(fp.master_gear.Proxy, CycloidGear)
and isinstance(fp.slave_gear.Proxy, CycloidGearRack)
):
# Rack gear logic remains unchanged
angle_master = fp.master_gear.Placement.Rotation.Angle * sum(
fp.master_gear.Placement.Rotation.Axis
)
dw_master = fp.master_gear.dw.Value
dw_master = fp.master_gear.pitch_diameter.Value
dw_slave = 0
dist = -(dw_master + dw_slave) / 2
mat0 = FreeCAD.Matrix() # unity matrix
mat0.move(FreeCAD.Vector(dist, 0, 0))
mat1 = FreeCAD.Matrix()
mat1.move(FreeCAD.Vector(0, np.deg2rad(fp.angle1.Value) * dw_master / 2, 0))
mat2 = FreeCAD.Matrix()
mat2.move(
FreeCAD.Vector(0, -np.deg2rad(fp.angle2.Value) * dw_master / 2, 0)
)
rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix()
mat0 = app.Matrix() # unity matrix
mat0.move(app.Vector(dist, 0, 0))
mat1 = app.Matrix()
mat1.move(app.Vector(0, np.deg2rad(fp.angle1.Value) * dw_master / 2, 0))
mat2 = app.Matrix()
mat2.move(app.Vector(0, -np.deg2rad(fp.angle2.Value) * dw_master / 2, 0))
rot = app.Rotation(app.Vector(0, 0, 1), fp.angle1).toMatrix()
mat3 = rot * mat2 * mat1 * mat0
mat3.move(fp.master_gear.Placement.Base)
fp.slave_gear.Placement = mat3
fp.slave_gear.purgeTouched()
if isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance(
fp.slave_gear.Proxy, CycloidGear
):
# Cycloid logic remains unchanged
angle_master = fp.master_gear.Placement.Rotation.Angle * sum(
fp.master_gear.Placement.Rotation.Axis
)
dw_master = fp.master_gear.dw
dw_slave = fp.slave_gear.dw
dw_master = fp.master_gear.pitch_diameter.Value
dw_slave = fp.slave_gear.pitch_diameter.Value
dist = (dw_master + dw_slave) / 2
mat0 = FreeCAD.Matrix() # unity matrix
trans = FreeCAD.Vector(dist, 0, 0)
master_stationary = getattr(fp, 'master_gear_stationary', True)
slave_stationary = getattr(fp, 'slave_gear_stationary', False)
if master_stationary and slave_stationary:
slave_position = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
# Master rotates by angle1
rot_master = app.Rotation(app.Vector(0, 0, 1), fp.angle1.Value)
fp.master_gear.Placement = app.Placement(fp.master_gear.Placement.Base, rot_master)
fp.master_gear.purgeTouched()
# Slave gets positioned at correct distance and rotates based on gear ratio
angle_slave = dw_master / dw_slave * fp.angle1.Value
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot_slave = app.Rotation(app.Vector(0, 0, 1), -angle_slave + angle3)
fp.slave_gear.Placement = app.Placement(slave_position, rot_slave)
fp.slave_gear.purgeTouched()
elif master_stationary and not slave_stationary:
mat0 = app.Matrix() # unity matrix
trans = app.Vector(dist, 0, 0)
mat0.move(trans)
rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix()
rot = app.Rotation(app.Vector(0, 0, 1), fp.angle1).toMatrix()
angle2 = dw_master / dw_slave * fp.angle1.Value
angle4 = dw_master / dw_slave * np.rad2deg(angle_master)
rot2 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle2).toMatrix()
angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180.0 / fp.slave_gear.teeth
rot3 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle3).toMatrix()
rot4 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -angle4).toMatrix()
rot2 = app.Rotation(app.Vector(0, 0, 1), angle2).toMatrix()
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
rot3 = app.Rotation(app.Vector(0, 0, 1), angle3).toMatrix()
rot4 = app.Rotation(app.Vector(0, 0, 1), -angle4).toMatrix()
mat1 = rot * mat0 * rot2 * rot3 * rot4
mat1.move(fp.master_gear.Placement.Base)
fp.slave_gear.Placement = mat1
fp.slave_gear.purgeTouched()
elif not master_stationary and slave_stationary:
mat0 = app.Matrix() # unity matrix
trans = app.Vector(dist, 0, 0)
mat0.move(trans)
rot = app.Rotation(app.Vector(0, 0, 1), -fp.angle1).toMatrix()
angle2 = -dw_slave / dw_master * fp.angle1.Value # master's rotation based on orbital motion
angle_slave = fp.slave_gear.Placement.Rotation.Angle * sum(
fp.slave_gear.Placement.Rotation.Axis
)
angle4 = -dw_slave / dw_master * np.rad2deg(angle_slave)
rot2 = app.Rotation(app.Vector(0, 0, 1), angle2).toMatrix()
rot4 = app.Rotation(app.Vector(0, 0, 1), -angle4).toMatrix()
mat1 = rot * mat0 * rot2 * rot4
mat1.move(fp.slave_gear.Placement.Base)
fp.master_gear.Placement = mat1
fp.master_gear.purgeTouched()
self._recomputing = True
app.ActiveDocument.recompute()
self._recomputing = False
def execute(self, fp):
self.onChanged(fp, None)
# We pass 'angle1' here to ensure the logic runs,
# as the 'prop' check was removed.
self.onChanged(fp, 'angle1')

176
freecad/gears/crowngear.py Normal file
View File

@@ -0,0 +1,176 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from freecad import part
from .basegear import BaseGear, fcvec
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class CrownGear(BaseGear):
"""
A crown gear (also known as a face gear or a contrate gear) is a gear
which has teeth that project at right angles to the face of the wheel.
In particular, a crown gear is a type of bevel gear where the pitch cone
angle is 90 degrees. https://en.wikipedia.org/wiki/Crown_gear
"""
def __init__(self, obj):
super(CrownGear, self).__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"other_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth of other gear"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP("App::Property", "module"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyLength",
"thickness",
"base",
QT_TRANSLATE_NOOP("App::Property", "thickness"),
)
obj.addProperty(
"App::PropertyAngle",
"pressure_angle",
"involute",
QT_TRANSLATE_NOOP("App::Property", "pressure angle"),
)
self.add_accuracy_properties(obj)
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.other_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.pressure_angle = "20. deg"
obj.height = "2. mm"
obj.thickness = "5 mm"
obj.num_profiles = 4
obj.preview_mode = True
self.obj = obj
obj.Proxy = self
app.Console.PrintMessage(
app.Qt.translate(
"Log",
"Gear module: Crown gear created, preview_mode = true for improved performance. "
"Set preview_mode property to false when ready to cut teeth.",
)
)
def add_accuracy_properties(self, obj):
obj.addProperty(
"App::PropertyInteger",
"num_profiles",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "number of profiles used for loft"),
)
obj.addProperty(
"App::PropertyBool",
"preview_mode",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "if true no boolean operation is done"),
)
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 generate_gear_shape(self, fp):
inner_diameter = fp.module.Value * fp.num_teeth
outer_diameter = inner_diameter + fp.height.Value * 2
inner_circle = part.Wire(part.makeCircle(inner_diameter / 2.0))
outer_circle = part.Wire(part.makeCircle(outer_diameter / 2.0))
inner_circle.reverse()
face = part.Face([outer_circle, inner_circle])
solid = face.extrude(app.Vector([0.0, 0.0, -fp.thickness.Value]))
if fp.preview_mode:
return solid
# cutting obj
alpha_w = np.deg2rad(fp.pressure_angle.Value)
m = fp.module.Value
t = fp.num_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 = part.Wire(part.makePolygon(list(map(fcvec, pts))))
polies.append(poly)
loft = part.makeLoft(polies, True)
rot = app.Matrix()
rot.rotateZ(2 * np.pi / t)
cut_shapes = []
for _ in range(t):
loft = loft.transformGeometry(rot)
cut_shapes.append(loft)
return solid.cut(cut_shapes)

View File

@@ -0,0 +1,284 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
from freecad import app
from freecad import part
import numpy as np
from pygears.cycloid_tooth import CycloidTooth
from pygears._functions import rotation
from .basegear import (
BaseGear,
points_to_wire,
insert_fillet,
helical_extrusion,
rotate_tooth,
)
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class CycloidGear(BaseGear):
"""FreeCAD gear"""
def __init__(self, obj):
super(CycloidGear, self).__init__(obj)
self.cycloid_tooth = CycloidTooth()
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP("App::Property", "module"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyInteger",
"numpoints",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "number of points for spline"),
)
obj.addProperty(
"App::PropertyPythonObject",
"gear",
"base",
QT_TRANSLATE_NOOP("App::Property", "the python object"),
)
self.add_helical_properties(obj)
self.add_fillet_properties(obj)
self.add_tolerance_properties(obj)
self.add_cycloid_properties(obj)
self.add_computed_properties(obj)
obj.gear = self.cycloid_tooth
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.setExpression(
"inner_diameter", "num_teeth / 2"
) # num_teeth/2 makes the hypocycloid a straight line to the center
obj.outer_diameter = 7.5 # we don't know the mating gear, so we just set the default to mesh with our default
obj.helix_angle = "0. deg"
obj.height = "5. mm"
obj.clearance = 0.25
obj.numpoints = 20
obj.backlash = "0.00 mm"
obj.double_helix = False
obj.head = 0
obj.head_fillet = 0
obj.root_fillet = 0
obj.Proxy = self
def onDocumentRestored(self, obj):
"""
backward compatibility functions
"""
if hasattr(obj, "dw"):
pitch_diameter = getattr(obj, "dw")
obj.addProperty(
"App::PropertyLength",
"pitch_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
1,
)
obj.pitch_diameter = pitch_diameter
obj.removeProperty("dw")
obj.setExpression(
"angular_backlash", "backlash / pitch_diameter * 360° / pi"
)
# replace beta with helix_angle
if hasattr(obj, "beta"):
helix_angle = getattr(obj, "beta")
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.helix_angle = helix_angle
obj.removeProperty("beta")
def add_helical_properties(self, obj):
obj.addProperty(
"App::PropertyBool",
"double_helix",
"helical",
QT_TRANSLATE_NOOP("App::Property", "double helix"),
)
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
def add_fillet_properties(self, obj):
obj.addProperty(
"App::PropertyFloatConstraint",
"head_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-head, radius = head_fillet x module",
),
).head_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyFloatConstraint",
"root_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-root, radius = root_fillet x module",
),
).root_fillet = (0.0, 0.0, 1000.0, 0.01)
def add_tolerance_properties(self, obj):
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP("App::Property", "clearance"),
)
obj.addProperty(
"App::PropertyLength",
"backlash",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property",
"The arc length on the pitch circle by which the tooth thickness is reduced.",
),
)
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "head_value * module_value = additional length of head"
),
)
def add_cycloid_properties(self, obj):
obj.addProperty(
"App::PropertyFloat",
"inner_diameter",
"cycloid",
QT_TRANSLATE_NOOP(
"App::Property", "inner_diameter divided by module (hypocycloid)"
),
)
obj.addProperty(
"App::PropertyFloat",
"outer_diameter",
"cycloid",
QT_TRANSLATE_NOOP(
"App::Property", "outer_diameter divided by module (epicycloid)"
),
)
def add_computed_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"pitch_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
)
obj.setExpression(
"pitch_diameter", "num_teeth * module"
) # calculate via expression to ease usage for placement
obj.setEditorMode(
"pitch_diameter", 1
) # set read-only after setting the expression, else it won't be visible. bug?
obj.addProperty(
"App::PropertyAngle",
"angular_backlash",
"computed",
QT_TRANSLATE_NOOP(
"App::Property",
"The angle by which this gear can turn without moving the mating gear.",
),
)
obj.setExpression(
"angular_backlash", "backlash / pitch_diameter * 360° / pi"
) # calculate via expression to ease usage for placement
obj.setEditorMode(
"angular_backlash", 1
) # set read-only after setting the expression, else it won't be visible. bug?
def generate_gear_shape(self, fp):
fp.gear.num_teeth = fp.num_teeth
fp.gear.m = fp.module.Value
fp.pitch_diameter = fp.module * fp.num_teeth
fp.gear.num_teeth_1 = fp.inner_diameter
fp.gear.num_teeth_2 = fp.outer_diameter
fp.gear.clearance = fp.clearance
fp.gear.head = fp.head
fp.gear.backlash = fp.backlash.Value
fp.gear._update()
pts = fp.gear.points(num=fp.numpoints)
rot = rotation(fp.gear.phipart)
rotated_pts = list(map(rot, pts))
pts.append([pts[-1][-1], rotated_pts[0][0]])
pts += rotated_pts
tooth = points_to_wire(pts)
edges = tooth.Edges
r_head = float(fp.head_fillet * fp.module)
r_root = float(fp.root_fillet * fp.module)
pos_head = [0, 2, 6]
pos_root = [4, 6]
edge_range = [1, 9]
for pos in pos_head:
edges = insert_fillet(edges, pos, r_head)
for pos in pos_root:
edges = insert_fillet(edges, pos, r_root)
edges = edges[edge_range[0] : edge_range[1]]
edges = [e for e in edges if e is not None]
tooth = part.Wire(edges)
profile = rotate_tooth(tooth, fp.num_teeth)
if fp.height.Value == 0:
return profile
base = part.Face(profile)
if fp.helix_angle.Value == 0:
return base.extrude(app.Vector(0, 0, fp.height.Value))
else:
beta = fp.helix_angle.Value * np.pi / 180
twist_angle = (
fp.height.Value * np.tan(beta) * 2 / fp.gear.d
)
return helical_extrusion(
base, fp.height.Value, twist_angle, fp.double_helix
)

View File

@@ -0,0 +1,291 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import os
import sys
from freecad import app
from freecad import part
import numpy as np
from pygears._functions import reflection
from .basegear import BaseGear, fcvec, points_to_wire, insert_fillet
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class CycloidGearRack(BaseGear):
"""FreeCAD gear rack"""
def __init__(self, obj):
super(CycloidGearRack, self).__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyLength",
"thickness",
"base",
QT_TRANSLATE_NOOP("App::Property", "thickness"),
)
obj.addProperty("App::PropertyLength", "module", "involute", "module")
obj.addProperty(
"App::PropertyBool",
"simplified",
"precision",
QT_TRANSLATE_NOOP(
"App::Property",
"if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.",
),
)
obj.addProperty(
"App::PropertyInteger",
"numpoints",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "number of points for spline"),
)
obj.addProperty(
"App::PropertyPythonObject",
"rack",
"base",
QT_TRANSLATE_NOOP("App::Property", "test"),
)
self.add_helical_properties(obj)
self.add_computed_properties(obj)
self.add_tolerance_properties(obj)
self.add_cycloid_properties(obj)
self.add_fillet_properties(obj)
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.inner_diameter = 7.5
obj.outer_diameter = 7.5
obj.height = "5. mm"
obj.thickness = "5 mm"
obj.helix_angle = "0. deg"
obj.clearance = 0.25
obj.head = 0.0
obj.add_endings = True
obj.simplified = False
obj.numpoints = 15
self.obj = obj
obj.Proxy = self
def onDocumentRestored(self, obj):
"""
backward compatibility functions
"""
# replace beta with helix_angle
if hasattr(obj, "beta"):
helix_angle = getattr(obj, "beta")
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.helix_angle = helix_angle
obj.removeProperty("beta")
def add_helical_properties(self, obj):
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.addProperty(
"App::PropertyBool",
"double_helix",
"helical",
QT_TRANSLATE_NOOP("App::Property", "double helix"),
)
def add_computed_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"transverse_pitch",
"computed",
QT_TRANSLATE_NOOP("App::Property", "pitch in the transverse plane"),
1,
)
obj.addProperty(
"App::PropertyBool",
"add_endings",
"base",
QT_TRANSLATE_NOOP(
"App::Property",
"if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank",
),
)
def add_tolerance_properties(self, obj):
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "head * module = additional length of head"
),
)
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "clearance * module = additional length of root"
),
)
def add_cycloid_properties(self, obj):
obj.addProperty(
"App::PropertyFloat",
"inner_diameter",
"cycloid",
QT_TRANSLATE_NOOP(
"App::Property", "inner_diameter divided by module (hypocycloid)"
),
)
obj.addProperty(
"App::PropertyFloat",
"outer_diameter",
"cycloid",
QT_TRANSLATE_NOOP(
"App::Property", "outer_diameter divided by module (epicycloid)"
),
)
def add_fillet_properties(self, obj):
obj.addProperty(
"App::PropertyFloatConstraint",
"head_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-head, radius = head_fillet x module",
),
).head_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyFloatConstraint",
"root_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-root, radius = root_fillet x module",
),
).root_fillet = (0.0, 0.0, 1000.0, 0.01)
def generate_gear_shape(self, obj):
numpoints = obj.numpoints
m = obj.module.Value
t = obj.thickness.Value
r_i = obj.inner_diameter / 2 * m
r_o = obj.outer_diameter / 2 * m
c = obj.clearance
h = obj.head
head_fillet = obj.head_fillet
root_fillet = obj.root_fillet
phi_i_end = np.arccos(1 - m / r_i * (1 + c))
phi_o_end = np.arccos(1 - m / r_o * (1 + h))
phi_i = np.linspace(phi_i_end, 0, numpoints)
phi_o = np.linspace(0, phi_o_end, numpoints)
y_i = r_i * (np.cos(phi_i) - 1)
y_o = r_o * (1 - np.cos(phi_o))
x_i = r_i * (np.sin(phi_i) - phi_i) - m * np.pi / 4
x_o = r_o * (phi_o - np.sin(phi_o)) - m * np.pi / 4
x = x_i.tolist()[:-1] + x_o.tolist()
y = y_i.tolist()[:-1] + y_o.tolist()
points = np.array([y, x]).T
mirror = reflection(0)
points_1 = mirror(points)[::-1]
line_1 = [points[-1], points_1[0]]
line_2 = [points_1[-1], np.array([-(1 + c) * m, m * np.pi / 2])]
line_0 = [np.array([-(1 + c) * m, -m * np.pi / 2]), points[0]]
tooth = points_to_wire([line_0, points, line_1, points_1, line_2])
edges = tooth.Edges
edges = insert_fillet(edges, 0, m * root_fillet)
edges = insert_fillet(edges, 2, m * head_fillet)
edges = insert_fillet(edges, 4, m * head_fillet)
edges = insert_fillet(edges, 6, m * root_fillet)
tooth_edges = [e for e in edges if e is not None]
p_end = np.array(tooth_edges[-2].lastVertex().Point[:-1])
p_start = np.array(tooth_edges[1].firstVertex().Point[:-1])
p_start += np.array([0, np.pi * m])
edge = points_to_wire([[p_end, p_start]]).Edges
tooth = part.Wire(tooth_edges[1:-1] + edge)
teeth = [tooth]
for i in range(obj.num_teeth - 1):
tooth = tooth.copy()
tooth.translate(app.Vector(0, np.pi * m, 0))
teeth.append(tooth)
teeth[-1] = part.Wire(teeth[-1].Edges[:-1])
if obj.add_endings:
teeth = [part.Wire(tooth_edges[0])] + teeth
last_edge = tooth_edges[-1]
last_edge.translate(app.Vector(0, np.pi * m * (obj.num_teeth - 1), 0))
teeth = teeth + [part.Wire(last_edge)]
p_start = np.array(teeth[0].Edges[0].firstVertex().Point[:-1])
p_end = np.array(teeth[-1].Edges[-1].lastVertex().Point[:-1])
p_start_1 = p_start - np.array([obj.thickness.Value, 0.0])
p_end_1 = p_end - np.array([obj.thickness.Value, 0.0])
line6 = [p_start, p_start_1]
line7 = [p_start_1, p_end_1]
line8 = [p_end_1, p_end]
bottom = points_to_wire([line6, line7, line8])
pol = part.Wire([bottom] + teeth)
if obj.height.Value == 0:
return pol
elif obj.helix_angle.Value == 0:
face = part.Face(part.Wire(pol))
return face.extrude(fcvec([0.0, 0.0, obj.height.Value]))
elif obj.double_helix:
beta = obj.helix_angle.Value * np.pi / 180.0
pol2 = part.Wire(pol)
pol2.translate(
fcvec([0.0, np.tan(beta) * obj.height.Value / 2, obj.height.Value / 2])
)
pol3 = part.Wire(pol)
pol3.translate(fcvec([0.0, 0.0, obj.height.Value]))
return part.makeLoft([pol, pol2, pol3], True, True)
else:
beta = obj.helix_angle.Value * np.pi / 180.0
pol2 = part.Wire(pol)
pol2.translate(
fcvec([0.0, np.tan(beta) * obj.height.Value, obj.height.Value])
)
return part.makeLoft([pol, pol2], True)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,311 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import math
import numpy as np
from freecad import app
from freecad import part
from pygears.bevel_tooth import BevelTooth
from pygears._functions import rotation
from .basegear import BaseGear, make_bspline_wire
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class HypoCycloidGear(BaseGear):
"""parameters:
pressure_angle: pressureangle, 10-30°
pitch_angle: cone angle, 0 < pitch_angle < pi/4
"""
def __init__(self, obj):
super(HypoCycloidGear, self).__init__(obj)
obj.addProperty(
"App::PropertyFloat",
"pin_circle_radius",
"gear_parameter",
QT_TRANSLATE_NOOP(
"App::Property", "Pin ball circle radius (overrides Tooth Pitch)"
),
)
obj.addProperty(
"App::PropertyFloat",
"roller_diameter",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "Roller Diameter"),
)
obj.addProperty(
"App::PropertyFloat",
"eccentricity",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "Eccentricity"),
)
obj.addProperty(
"App::PropertyAngle",
"pressure_angle_lim",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "Pressure angle limit"),
)
obj.addProperty(
"App::PropertyFloat",
"pressure_angle_offset",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "Offset in pressure angle"),
)
obj.addProperty(
"App::PropertyInteger",
"teeth_number",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "Number of teeth in Cam"),
)
obj.addProperty(
"App::PropertyInteger",
"segment_count",
"gear_parameter",
QT_TRANSLATE_NOOP(
"App::Property", "Number of points used for spline interpolation"
),
)
obj.addProperty(
"App::PropertyLength",
"hole_radius",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "Center hole's radius"),
)
obj.addProperty(
"App::PropertyBool",
"show_pins",
"Pins",
QT_TRANSLATE_NOOP("App::Property", "Create pins in place"),
)
obj.addProperty(
"App::PropertyLength",
"pin_height",
"Pins",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyBool",
"center_pins",
"Pins",
QT_TRANSLATE_NOOP("App::Property", "Center pin Z axis to generated disks"),
)
obj.addProperty(
"App::PropertyBool",
"show_disk0",
"Disks",
QT_TRANSLATE_NOOP("App::Property", "Show main cam disk"),
)
obj.addProperty(
"App::PropertyBool",
"show_disk1",
"Disks",
QT_TRANSLATE_NOOP("App::Property", "Show another reversed cam disk on top"),
)
obj.addProperty(
"App::PropertyLength",
"disk_height",
"Disks",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.pin_circle_radius = 66
obj.roller_diameter = 3
obj.eccentricity = 1.5
obj.pressure_angle_lim = "50.0 deg"
obj.pressure_angle_offset = 0.01
obj.teeth_number = 42
obj.segment_count = 42
obj.hole_radius = "30. mm"
obj.show_pins = True
obj.pin_height = "20. mm"
obj.center_pins = True
obj.show_disk0 = True
obj.show_disk1 = True
obj.disk_height = "10. mm"
self.obj = obj
obj.Proxy = self
def to_polar(self, x, y):
return (x**2 + y**2) ** 0.5, math.atan2(y, x)
def to_rect(self, r, a):
return r * math.cos(a), r * math.sin(a)
def calcyp(self, p, a, e, n):
return math.atan(math.sin(n * a) / (math.cos(n * a) + (n * p) / (e * (n + 1))))
def calc_x(self, p, d, e, n, a):
return (
(n * p) * math.cos(a)
+ e * math.cos((n + 1) * a)
- d / 2 * math.cos(self.calcyp(p, a, e, n) + a)
)
def calc_y(self, p, d, e, n, a):
return (
(n * p) * math.sin(a)
+ e * math.sin((n + 1) * a)
- d / 2 * math.sin(self.calcyp(p, a, e, n) + a)
)
def calc_pressure_angle(self, p, d, n, a):
ex = 2**0.5
r3 = p * n
rg = r3 / ex
pp = rg * (ex**2 + 1 - 2 * ex * math.cos(a)) ** 0.5 - d / 2
return math.asin((r3 * math.cos(a) - rg) / (pp + d / 2)) * 180 / math.pi
def calc_pressure_limit(self, p, d, e, n, a):
ex = 2**0.5
r3 = p * n
rg = r3 / ex
q = (r3**2 + rg**2 - 2 * r3 * rg * math.cos(a)) ** 0.5
x = rg - e + (q - d / 2) * (r3 * math.cos(a) - rg) / q
y = (q - d / 2) * r3 * math.sin(a) / q
return (x**2 + y**2) ** 0.5
def check_limit(self, x, y, maxrad, minrad, offset):
r, a = self.to_polar(x, y)
if (r > maxrad) or (r < minrad):
r = r - offset
x, y = self.to_rect(r, a)
return x, y
def generate_gear_shape(self, fp):
b = fp.pin_circle_radius
d = fp.roller_diameter
e = fp.eccentricity
n = fp.teeth_number
p = b / n
s = fp.segment_count
ang = fp.pressure_angle_lim
c = fp.pressure_angle_offset
q = 2 * math.pi / float(s)
# Find the pressure angle limit circles
minAngle = -1.0
maxAngle = -1.0
for i in range(0, 180):
x = self.calc_pressure_angle(p, d, n, i * math.pi / 180.0)
if (x < ang) and (minAngle < 0):
minAngle = float(i)
if (x < -ang) and (maxAngle < 0):
maxAngle = float(i - 1)
minRadius = self.calc_pressure_limit(p, d, e, n, minAngle * math.pi / 180.0)
maxRadius = self.calc_pressure_limit(p, d, e, n, maxAngle * math.pi / 180.0)
# unused
# part.Wire(part.makeCircle(minRadius, app.Vector(-e, 0, 0)))
# part.Wire(part.makeCircle(maxRadius, app.Vector(-e, 0, 0)))
app.Console.PrintMessage(app.Qt.translate("Log", "Generating cam disk\n"))
# generate the cam profile - note: shifted in -x by eccentricicy amount
i = 0
x = self.calc_x(p, d, e, n, q * i / float(n))
y = self.calc_y(p, d, e, n, q * i / n)
x, y = self.check_limit(x, y, maxRadius, minRadius, c)
points = [app.Vector(x - e, y, 0)]
for i in range(0, s):
x = self.calc_x(p, d, e, n, q * (i + 1) / n)
y = self.calc_y(p, d, e, n, q * (i + 1) / n)
x, y = self.check_limit(x, y, maxRadius, minRadius, c)
points.append([x - e, y, 0])
wi = make_bspline_wire([points])
wires = []
mat = app.Matrix()
mat.move(app.Vector(e, 0.0, 0.0))
mat.rotateZ(2 * np.pi / n)
mat.move(app.Vector(-e, 0.0, 0.0))
for _ in range(n):
wi = wi.transformGeometry(mat)
wires.append(wi)
cam = part.Face(part.Wire(wires))
# add a circle in the center of the cam
if fp.hole_radius.Value:
centerCircle = part.Face(
part.Wire(part.makeCircle(fp.hole_radius.Value, app.Vector(-e, 0, 0)))
)
cam = cam.cut(centerCircle)
to_be_fused = []
if fp.show_disk0:
if fp.disk_height.Value == 0:
to_be_fused.append(cam)
else:
to_be_fused.append(cam.extrude(app.Vector(0, 0, fp.disk_height.Value)))
# secondary cam disk
if fp.show_disk1:
app.Console.PrintMessage(
app.Qt.translate("Log", "Generating secondary cam disk\n")
)
second_cam = cam.copy()
mat = app.Matrix()
mat.rotateZ(np.pi)
mat.move(app.Vector(-e, 0, 0))
if n % 2 == 0:
mat.rotateZ(np.pi / n)
mat.move(app.Vector(e, 0, 0))
second_cam = second_cam.transformGeometry(mat)
if fp.disk_height.Value == 0:
to_be_fused.append(second_cam)
else:
to_be_fused.append(
second_cam.extrude(app.Vector(0, 0, -fp.disk_height.Value))
)
# pins
if fp.show_pins:
app.Console.PrintMessage(app.Qt.translate("Log", "Generating pins\n"))
pins = []
for i in range(0, n + 1):
x = p * n * math.cos(2 * math.pi / (n + 1) * i)
y = p * n * math.sin(2 * math.pi / (n + 1) * i)
pins.append(part.Wire(part.makeCircle(d / 2, app.Vector(x, y, 0))))
pins = part.Face(pins)
z_offset = -fp.pin_height.Value / 2
if fp.center_pins:
if fp.show_disk0 and not fp.show_disk1:
z_offset += fp.disk_height.Value / 2
elif not fp.show_disk0 and fp.show_disk1:
z_offset += -fp.disk_height.Value / 2
# extrude
if z_offset != 0:
pins.translate(app.Vector(0, 0, z_offset))
if fp.pin_height != 0:
pins = pins.extrude(app.Vector(0, 0, fp.pin_height.Value))
to_be_fused.append(pins)
if to_be_fused:
return part.makeCompound(to_be_fused)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 95 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -18,21 +18,14 @@
import os
import sys
import FreeCADGui as Gui
import FreeCAD as App
from freecad import app
from freecad import gui
__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"
)
# Add translations path
gui.addLanguagePath(os.path.join(__dirname__, "translations"))
gui.updateLocale()
if sys.version_info[0] == 3 and sys.version_info[1] >= 11:
# only works with 0.21.2 and above
@@ -43,19 +36,15 @@ if sys.version_info[0] == 3 and sys.version_info[1] >= 11:
FC_COMMIT_REQUIRED = 33772
# Check FreeCAD version
App.Console.PrintLog("Checking FreeCAD version\n")
ver = App.Version()
app.Console.PrintLog(app.Qt.translate("Log", "Checking FreeCAD version\n"))
ver = app.Version()
major_ver = int(ver[0])
minor_vers = ver[1].split(".")
minor_ver = int(minor_vers[0])
if minor_vers[1:] and minor_vers[1]:
patch_ver = int(minor_vers[1])
else:
patch_ver = 0
gitver = ver[2].split()
minor_ver = int(ver[1])
patch_ver = int(ver[2])
gitver = ver[3].split()
if gitver:
gitver = gitver[0]
if gitver and gitver != "Unknown":
if gitver and gitver != "Unknown" and gitver.isdigit():
gitver = int(gitver)
else:
# If we don't have the git version, assume it's OK.
@@ -77,8 +66,11 @@ if sys.version_info[0] == 3 and sys.version_info[1] >= 11:
)
)
):
App.Console.PrintWarning(
"FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above\n".format(
app.Console.PrintWarning(
app.Qt.translate(
"Log",
"FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above\n",
).format(
int(ver[0]),
minor_ver,
patch_ver,
@@ -90,32 +82,34 @@ if sys.version_info[0] == 3 and sys.version_info[1] >= 11:
)
)
class GearWorkbench(Workbench):
class GearWorkbench(gui.Workbench):
"""A freecad workbench aiming at gear design"""
MenuText = "Gear"
ToolTip = "Gear Workbench"
MenuText = app.Qt.translate("Workbench", "Gear")
ToolTip = app.Qt.translate("Workbench", "Gear Workbench")
Icon = os.path.join(__dirname__, "icons", "gearworkbench.svg")
commands = [
"CreateInvoluteGear",
"CreateInternalInvoluteGear",
"CreateInvoluteRack",
"CreateCycloidGear",
"CreateCycloidRack",
"CreateBevelGear",
"CreateCrownGear",
"CreateWormGear",
"CreateTimingGearT",
"CreateTimingGear",
"CreateLanternGear",
"CreateHypoCycloidGear",
"CreateGearConnector",
"FCGear_InvoluteGear",
"FCGear_InternalInvoluteGear",
"FCGear_InvoluteRack",
"FCGear_CycloidGear",
"FCGear_CycloidRack",
"FCGear_BevelGear",
"FCGear_CrownGear",
"FCGear_WormGear",
"FCGear_TimingGearT",
"FCGear_TimingGear",
"FCGear_LanternGear",
"FCGear_HypoCycloidGear",
"FCGear_GearConnector",
]
def GetClassName(self):
return "Gui::PythonWorkbench"
def Initialize(self):
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
from .commands import (
CreateCycloidGear,
CreateInvoluteGear,
@@ -129,24 +123,24 @@ class GearWorkbench(Workbench):
CreateLanternGear,
CreateHypoCycloidGear,
CreateCycloidRack,
CreateGearConnector
CreateGearConnector,
)
self.appendToolbar("Gear", self.commands)
self.appendMenu("Gear", self.commands)
Gui.addCommand("CreateInvoluteGear", CreateInvoluteGear())
Gui.addCommand("CreateInternalInvoluteGear", CreateInternalInvoluteGear())
Gui.addCommand("CreateCycloidGear", CreateCycloidGear())
Gui.addCommand("CreateCycloidRack", CreateCycloidRack())
Gui.addCommand("CreateBevelGear", CreateBevelGear())
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())
Gui.addCommand("CreateGearConnector", CreateGearConnector())
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Gear"), self.commands)
self.appendMenu(QT_TRANSLATE_NOOP("Workbench", "Gear"), self.commands)
gui.addCommand("FCGear_InvoluteGear", CreateInvoluteGear())
gui.addCommand("FCGear_InternalInvoluteGear", CreateInternalInvoluteGear())
gui.addCommand("FCGear_CycloidGear", CreateCycloidGear())
gui.addCommand("FCGear_CycloidRack", CreateCycloidRack())
gui.addCommand("FCGear_BevelGear", CreateBevelGear())
gui.addCommand("FCGear_InvoluteRack", CreateInvoluteRack())
gui.addCommand("FCGear_CrownGear", CreateCrownGear())
gui.addCommand("FCGear_WormGear", CreateWormGear())
gui.addCommand("FCGear_TimingGearT", CreateTimingGearT())
gui.addCommand("FCGear_TimingGear", CreateTimingGear())
gui.addCommand("FCGear_LanternGear", CreateLanternGear())
gui.addCommand("FCGear_HypoCycloidGear", CreateHypoCycloidGear())
gui.addCommand("FCGear_GearConnector", CreateGearConnector())
def Activated(self):
pass
@@ -155,4 +149,4 @@ class GearWorkbench(Workbench):
pass
Gui.addWorkbench(GearWorkbench())
gui.addWorkbench(GearWorkbench())

View File

@@ -0,0 +1,407 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from freecad import part
from pygears.involute_tooth import InvoluteTooth
from pygears._functions import rotation
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
from .basegear import (
BaseGear,
points_to_wire,
insert_fillet,
helical_extrusion,
rotate_tooth,
)
class InternalInvoluteGear(BaseGear):
"""FreeCAD internal involute gear
Using the same tooth as the external, just turning it inside-out:
addedum becomes dedendum, clearance becomes head, negate the backslash, ...
"""
def __init__(self, obj):
super(InternalInvoluteGear, self).__init__(obj)
self.involute_tooth = InvoluteTooth()
obj.addProperty(
"App::PropertyBool",
"simple",
"precision",
QT_TRANSLATE_NOOP("App::Property", "simple"),
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP(
"App::Property",
"normal module if properties_from_tool=True, else it's the transverse module.",
),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyLength",
"thickness",
"base",
QT_TRANSLATE_NOOP("App::Property", "thickness"),
)
obj.addProperty(
"App::PropertyInteger",
"numpoints",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "number of points for spline"),
)
obj.addProperty(
"App::PropertyPythonObject",
"gear",
"base",
QT_TRANSLATE_NOOP("App::Property", "test"),
)
self.add_involute_properties(obj)
self.add_tolerance_properties(obj)
self.add_fillet_properties(obj)
self.add_computed_properties(obj)
self.add_limiting_diameter_properties(obj)
self.add_helical_properties(obj)
obj.gear = self.involute_tooth
obj.simple = False
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.shift = 0.0
obj.pressure_angle = "20. deg"
obj.helix_angle = "0. deg"
obj.height = "5. mm"
obj.thickness = "5 mm"
obj.clearance = 0.25
obj.head = -0.4 # using head=0 and shift=0.5 may be better, but makes placeing the pinion less intuitive
obj.numpoints = 20
obj.double_helix = False
obj.backlash = "0.00 mm"
obj.reversed_backlash = False
obj.properties_from_tool = False
obj.head_fillet = 0
obj.root_fillet = 0
self.obj = obj
obj.Proxy = self
def onDocumentRestored(self, obj):
"""
backward compatibility functions
"""
if hasattr(obj, "dw"):
pitch_diameter = getattr(obj, "dw")
obj.addProperty(
"App::PropertyLength",
"pitch_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
8,
)
obj.pitch_diameter = pitch_diameter
obj.removeProperty("dw")
obj.setExpression(
"angular_backlash", "backlash / pitch_diameter * 360° / pi"
)
# replace da with addendum_diameter
if hasattr(obj, "da"):
addendum_diameter = getattr(obj, "da")
obj.addProperty(
"App::PropertyLength",
"addendum_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The addendum diameter."),
8,
)
obj.addendum_diameter = addendum_diameter
obj.removeProperty("da")
# replace df with root_diameter
if hasattr(obj, "df"):
root_diameter = getattr(obj, "df")
obj.addProperty(
"App::PropertyLength",
"root_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The root diameter."),
8,
)
obj.root_diameter = root_diameter
obj.removeProperty("df")
# replace beta with helix_angle
if hasattr(obj, "beta"):
helix_angle = getattr(obj, "beta")
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.helix_angle = helix_angle
obj.removeProperty("beta")
def add_limiting_diameter_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"addendum_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The addendum diameter"),
1,
)
obj.addProperty(
"App::PropertyLength",
"root_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The root diameter"),
1,
)
def add_computed_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"pitch_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
8,
)
obj.addProperty(
"App::PropertyAngle",
"angular_backlash",
"computed",
QT_TRANSLATE_NOOP(
"App::Property",
"The angle by which this gear can turn without moving the mating gear.",
),
8,
)
obj.setExpression(
"angular_backlash", "backlash / pitch_diameter * 360° / pi"
) # calculate via expression to ease usage for placement
obj.setEditorMode(
"angular_backlash", 1
) # set read-only after setting the expression, else it won't be visible. bug?
obj.addProperty(
"App::PropertyLength",
"transverse_pitch",
"computed",
QT_TRANSLATE_NOOP("App::Property", "transverse_pitch"),
8,
)
obj.addProperty(
"App::PropertyLength",
"outside_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "Outside diameter"),
8,
)
def add_fillet_properties(self, obj):
obj.addProperty(
"App::PropertyFloatConstraint",
"head_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-head, radius = head_fillet x module",
),
).head_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyFloatConstraint",
"root_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-root, radius = root_fillet x module",
),
).root_fillet = (0.0, 0.0, 1000.0, 0.01)
def add_tolerance_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"backlash",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property",
"The arc length on the pitch circle by which the tooth thickness is reduced.",
),
)
obj.addProperty(
"App::PropertyBool",
"reversed_backlash",
"tolerance",
QT_TRANSLATE_NOOP("App::Property", "backlash direction"),
)
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property",
"head_value * module_value = additional length of head",
),
)
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP("App::Property", "clearance"),
)
def add_involute_properties(self, obj):
obj.addProperty(
"App::PropertyFloat",
"shift",
"involute",
QT_TRANSLATE_NOOP("App::Property", "shift"),
)
obj.addProperty(
"App::PropertyAngle",
"pressure_angle",
"involute",
QT_TRANSLATE_NOOP("App::Property", "pressure angle"),
)
def add_helical_properties(self, obj):
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.addProperty(
"App::PropertyBool",
"double_helix",
"helical",
QT_TRANSLATE_NOOP("App::Property", "double helix"),
)
obj.addProperty(
"App::PropertyBool",
"properties_from_tool",
"helical",
QT_TRANSLATE_NOOP(
"App::Property",
"if helix_angle is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear",
),
)
def generate_gear_shape(self, fp):
fp.gear.double_helix = fp.double_helix
fp.gear.m_n = fp.module.Value
fp.gear.num_teeth = fp.num_teeth
fp.gear.undercut = False # no undercut for internal gears
fp.gear.shift = fp.shift
fp.gear.pressure_angle = fp.pressure_angle.Value * np.pi / 180.0
fp.gear.beta = fp.helix_angle.Value * np.pi / 180
fp.gear.clearance = fp.head # swap head and clearance to become "internal"
fp.gear.backlash = (
fp.backlash.Value * (fp.reversed_backlash - 0.5) * 2.0
) # negate "reversed_backslash", for "internal"
fp.gear.head = fp.clearance # swap head and clearance to become "internal"
fp.gear.properties_from_tool = fp.properties_from_tool
fp.gear._update()
fp.pitch_diameter = "{}mm".format(fp.gear.dw)
# computed properties
fp.transverse_pitch = "{}mm".format(fp.gear.pitch)
fp.outside_diameter = fp.pitch_diameter + 2 * fp.thickness
# checksbackwardcompatibility:
if not "addendum_diameter" in fp.PropertiesList:
self.add_limiting_diameter_properties(fp)
fp.addendum_diameter = "{}mm".format(fp.gear.df) # swap addendum and dedendum for "internal"
fp.root_diameter = "{}mm".format(fp.gear.da) # swap addendum and dedendum for "internal"
outer_circle = part.Wire(part.makeCircle(fp.outside_diameter / 2.0))
outer_circle.reverse()
if not fp.simple:
# head-fillet:
pts = fp.gear.points(num=fp.numpoints)
rot = rotation(fp.gear.phipart)
rotated_pts = list(map(rot, pts))
pts.append([pts[-1][-1], rotated_pts[0][0]])
pts += rotated_pts
tooth = points_to_wire(pts)
r_head = float(fp.root_fillet * fp.module) # reversing head
r_root = float(fp.head_fillet * fp.module) # and foot
edges = tooth.Edges
if len(tooth.Edges) == 11:
pos_head = [1, 3, 9]
pos_root = [6, 8]
edge_range = [2, 12]
else:
pos_head = [0, 2, 6]
pos_root = [4, 6]
edge_range = [1, 9]
for pos in pos_head:
edges = insert_fillet(edges, pos, r_head)
for pos in pos_root:
try:
edges = insert_fillet(edges, pos, r_root)
except RuntimeError:
edges.pop(8)
edges.pop(6)
edge_range = [2, 10]
pos_root = [5, 7]
for pos in pos_root:
edges = insert_fillet(edges, pos, r_root)
break
edges = edges[edge_range[0] : edge_range[1]]
edges = [e for e in edges if e is not None]
tooth = part.Wire(edges)
profile = rotate_tooth(tooth, fp.num_teeth)
if fp.height.Value == 0:
return part.makeCompound([outer_circle, profile])
base = part.Face([outer_circle, profile])
if fp.gear.beta == 0:
return base.extrude(app.Vector(0, 0, fp.height.Value))
else:
twist_angle = fp.height.Value * np.tan(fp.gear.beta) * 2 / fp.gear.d
return helical_extrusion(
base, fp.height.Value, twist_angle, fp.double_helix
)
else:
inner_circle = part.Wire(part.makeCircle(fp.pitch_diameter / 2.0))
inner_circle.reverse()
base = part.Face([outer_circle, inner_circle])
return base.extrude(app.Vector(0, 0, fp.height.Value))

View File

@@ -0,0 +1,466 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from freecad import part
from pygears.involute_tooth import InvoluteTooth
from pygears._functions import rotation
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
from .basegear import (
BaseGear,
points_to_wire,
insert_fillet,
helical_extrusion,
rotate_tooth,
)
class InvoluteGear(BaseGear):
"""FreeCAD gear"""
def __init__(self, obj):
super(InvoluteGear, self).__init__(obj)
self.involute_tooth = InvoluteTooth()
obj.addProperty(
"App::PropertyPythonObject",
"gear",
"base",
QT_TRANSLATE_NOOP("App::Property", "python gear object"),
)
self.add_gear_properties(obj)
self.add_fillet_properties(obj)
self.add_helical_properties(obj)
self.add_computed_properties(obj)
self.add_tolerance_properties(obj)
self.add_accuracy_properties(obj)
self.add_hole_properties(obj)
obj.gear = self.involute_tooth
obj.simple = False
obj.undercut = False
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.shift = 0.0
obj.pressure_angle = "20. deg"
obj.helix_angle = "0. deg"
obj.height = "5. mm"
obj.clearance = 0.25
obj.head = 0.0
obj.numpoints = 20
obj.double_helix = False
obj.backlash = "0.00 mm"
obj.reversed_backlash = False
obj.properties_from_tool = False
obj.head_fillet = 0
obj.root_fillet = 0
obj.axle_hole = False
obj.axle_holesize = "10.mm"
obj.offset_hole = False
obj.offset_holesize = "10.mm"
obj.offset_holeoffset = "10.mm"
self.obj = obj
obj.Proxy = self
self.compute_traverse_properties(obj)
def onDocumentRestored(self, obj):
"""
backward compatibility functions
"""
# replace dw with pitch_diameter
if hasattr(obj, "dw"):
pitch_diameter = getattr(obj, "dw")
obj.addProperty(
"App::PropertyLength",
"pitch_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
8,
)
obj.pitch_diameter = pitch_diameter
obj.removeProperty("dw")
obj.setExpression(
"angular_backlash", "backlash / pitch_diameter * 360° / pi"
)
# replace da with addendum_diameter
if hasattr(obj, "da"):
addendum_diameter = getattr(obj, "da")
obj.addProperty(
"App::PropertyLength",
"addendum_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The addendum diameter."),
8,
)
obj.addendum_diameter = addendum_diameter
obj.removeProperty("da")
# replace df with root_diameter
if hasattr(obj, "df"):
root_diameter = getattr(obj, "df")
obj.addProperty(
"App::PropertyLength",
"root_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The root diameter."),
8,
)
obj.root_diameter = root_diameter
obj.removeProperty("df")
# replace beta with helix_angle
if hasattr(obj, "beta"):
helix_angle = getattr(obj, "beta")
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.helix_angle = helix_angle
obj.removeProperty("beta")
def add_hole_properties(self, obj):
"""Add properties for the central hole"""
obj.addProperty(
"App::PropertyBool",
"axle_hole",
"hole",
QT_TRANSLATE_NOOP("App::Property", "enable central hole for axle"),
)
obj.addProperty(
"App::PropertyLength",
"axle_holesize",
"hole",
QT_TRANSLATE_NOOP("App::Property", "diameter of central hole for axle"),
)
obj.addProperty(
"App::PropertyBool",
"offset_hole",
"hole",
QT_TRANSLATE_NOOP("App::Property", "enable offset hole"),
)
obj.addProperty(
"App::PropertyLength",
"offset_holesize",
"hole",
QT_TRANSLATE_NOOP("App::Property", "diameter of offset hole"),
)
obj.addProperty(
"App::PropertyLength",
"offset_holeoffset",
"hole",
QT_TRANSLATE_NOOP("App::Property", "offset of offset hole"),
)
def add_gear_properties(self, obj):
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP(
"App::Property",
"normal module if properties_from_tool=True, else it's the transverse module.",
),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyAngle",
"pressure_angle",
"involute",
QT_TRANSLATE_NOOP("App::Property", "pressure angle"),
)
obj.addProperty(
"App::PropertyFloat",
"shift",
"involute",
QT_TRANSLATE_NOOP("App::Property", "shift"),
)
def add_fillet_properties(self, obj):
obj.addProperty(
"App::PropertyBool",
"undercut",
"fillets",
QT_TRANSLATE_NOOP("App::Property", "undercut"),
)
obj.addProperty(
"App::PropertyFloatConstraint",
"head_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-head, radius = head_fillet x module",
),
).head_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyFloatConstraint",
"root_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-root, radius = root_fillet x module",
),
).root_fillet = (0.0, 0.0, 1000.0, 0.01)
def add_helical_properties(self, obj):
obj.addProperty(
"App::PropertyBool",
"properties_from_tool",
"helical",
QT_TRANSLATE_NOOP(
"App::Property",
"if helix_angle is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear",
),
)
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.addProperty(
"App::PropertyBool",
"double_helix",
"helical",
QT_TRANSLATE_NOOP("App::Property", "double helix"),
)
def add_computed_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"addendum_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The outside diameter"),
8,
)
obj.addProperty(
"App::PropertyLength",
"root_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The root diameter"),
8,
)
self.add_traverse_module_property(obj)
obj.addProperty(
"App::PropertyLength",
"pitch_diameter",
"computed",
QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."),
8,
)
obj.addProperty(
"App::PropertyAngle",
"angular_backlash",
"computed",
QT_TRANSLATE_NOOP(
"App::Property",
"The angle by which this gear can turn without moving the mating gear.",
),
8,
)
obj.setExpression(
"angular_backlash", "backlash / pitch_diameter * 360° / pi"
) # calculate via expression to ease usage for placement
obj.setEditorMode(
"angular_backlash", 1
) # set read-only after setting the expression, else it won't be visible. bug?
obj.addProperty(
"App::PropertyLength",
"transverse_pitch",
"computed",
QT_TRANSLATE_NOOP("App::Property", "transverse_pitch"),
8,
)
def add_tolerance_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"backlash",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property",
"The arc length on the pitch circle by which the tooth thickness is reduced.",
),
)
obj.addProperty(
"App::PropertyBool",
"reversed_backlash",
"tolerance",
QT_TRANSLATE_NOOP("App::Property", "backlash direction"),
)
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP("App::Property", "clearance"),
)
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "head_value * module_value = additional length of head"
),
)
def add_accuracy_properties(self, obj):
obj.addProperty(
"App::PropertyBool",
"simple",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "simple"),
)
obj.addProperty(
"App::PropertyInteger",
"numpoints",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "number of points for spline"),
)
def add_traverse_module_property(self, obj):
obj.addProperty(
"App::PropertyLength",
"traverse_module",
"computed",
QT_TRANSLATE_NOOP("App::Property", "traverse module of the generated gear"),
8,
)
def compute_traverse_properties(self, obj):
# traverse_module added recently, if old freecad doc is loaded without it, it will not exist when generate_gear_shape() is called
if not hasattr(obj, "traverse_module"):
self.add_traverse_module_property(obj)
if obj.properties_from_tool:
obj.traverse_module = obj.module / np.cos(obj.gear.beta)
else:
obj.traverse_module = obj.module
obj.transverse_pitch = "{}mm".format(obj.gear.pitch)
obj.addendum_diameter = "{}mm".format(obj.gear.da)
obj.root_diameter = "{}mm".format(obj.gear.df)
obj.pitch_diameter = "{}mm".format(obj.gear.dw)
def generate_gear_shape(self, obj):
obj.gear.double_helix = obj.double_helix
obj.gear.m_n = obj.module.Value
obj.gear.undercut = obj.undercut
obj.gear.shift = obj.shift
obj.gear.pressure_angle = obj.pressure_angle.Value * np.pi / 180.0
obj.gear.beta = obj.helix_angle.Value * np.pi / 180
obj.gear.clearance = obj.clearance
obj.gear.backlash = obj.backlash.Value * (-obj.reversed_backlash + 0.5) * 2.0
obj.gear.head = obj.head
obj.gear.properties_from_tool = obj.properties_from_tool
obj.gear.num_teeth = obj.num_teeth
obj.gear._update()
self.compute_traverse_properties(obj)
if not obj.simple:
pts = obj.gear.points(num=obj.numpoints)
rot = rotation(obj.gear.phipart)
rotated_pts = list(map(rot, pts))
pts.append([pts[-1][-1], rotated_pts[0][0]])
pts += rotated_pts
tooth = points_to_wire(pts)
edges = tooth.Edges
# head-fillet:
r_head = float(obj.head_fillet * obj.module)
r_root = float(obj.root_fillet * obj.module)
if obj.undercut and r_root != 0.0:
r_root = 0.0
app.Console.PrintWarning(
"root fillet is not allowed if undercut is computed"
)
if len(tooth.Edges) == 11:
pos_head = [1, 3, 9]
pos_root = [6, 8]
edge_range = [2, 12]
else:
pos_head = [0, 2, 6]
pos_root = [4, 6]
edge_range = [1, 9]
for pos in pos_head:
edges = insert_fillet(edges, pos, r_head)
for pos in pos_root:
try:
edges = insert_fillet(edges, pos, r_root)
except RuntimeError:
edges.pop(8)
edges.pop(6)
edge_range = [2, 10]
pos_root = [5, 7]
for pos in pos_root:
edges = insert_fillet(edges, pos, r_root)
break
edges = edges[edge_range[0] : edge_range[1]]
edges = [e for e in edges if e is not None]
tooth = part.Wire(edges)
profile = rotate_tooth(tooth, obj.num_teeth)
if obj.height.Value == 0:
gear_shape = profile
else:
base = part.Face(profile)
if obj.gear.beta == 0:
gear_shape = base.extrude(app.Vector(0, 0, obj.height.Value))
else:
twist_angle = obj.height.Value * np.tan(obj.gear.beta) * 2 / obj.gear.d
gear_shape = helical_extrusion(
base, obj.height.Value, twist_angle, obj.double_helix
)
else:
rw = obj.gear.dw / 2
gear_shape = part.makeCylinder(rw, obj.height.Value)
if hasattr(obj, "axle_hole"):
if obj.axle_hole and obj.axle_holesize.Value > 0:
axle_hole = part.makeCylinder(obj.axle_holesize.Value/2, obj.height.Value*2)
axle_hole.Placement.Base = app.Vector(0, 0, -obj.height.Value/2)
gear_shape = gear_shape.cut(axle_hole)
if obj.offset_hole and obj.offset_holesize.Value > 0:
hole = part.makeCylinder(obj.offset_holesize.Value/2, obj.height.Value*2)
hole.Placement.Base = app.Vector(-obj.offset_holeoffset.Value, 0, -obj.height.Value/2)
gear_shape = gear_shape.cut(hole)
return gear_shape

View File

@@ -0,0 +1,307 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from freecad import part
from pygears.involute_tooth import InvoluteRack
from .basegear import BaseGear, fcvec, points_to_wire, insert_fillet
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class InvoluteGearRack(BaseGear):
"""FreeCAD gear rack"""
def __init__(self, obj):
super(InvoluteGearRack, self).__init__(obj)
self.involute_rack = InvoluteRack()
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP("App::Property", "module"),
)
obj.addProperty(
"App::PropertyLength",
"thickness",
"base",
QT_TRANSLATE_NOOP("App::Property", "thickness"),
)
obj.addProperty(
"App::PropertyBool",
"simplified",
"precision",
QT_TRANSLATE_NOOP(
"App::Property",
"if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.",
),
)
obj.addProperty(
"App::PropertyPythonObject",
"rack",
"base",
QT_TRANSLATE_NOOP("App::Property", "test"),
)
self.add_helical_properties(obj)
self.add_computed_properties(obj)
self.add_tolerance_properties(obj)
self.add_involute_properties(obj)
self.add_fillet_properties(obj)
obj.rack = self.involute_rack
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.pressure_angle = "20. deg"
obj.height = "5. mm"
obj.thickness = "5 mm"
obj.helix_angle = "0. deg"
obj.clearance = 0.25
obj.head = 0.0
obj.properties_from_tool = False
obj.add_endings = True
obj.simplified = False
self.obj = obj
obj.Proxy = self
def onDocumentRestored(self, obj):
"""
backward compatibility functions
"""
# replace beta with helix_angle
if hasattr(obj, "beta"):
helix_angle = getattr(obj, "beta")
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.helix_angle = helix_angle
obj.removeProperty("beta")
def add_helical_properties(self, obj):
obj.addProperty(
"App::PropertyBool",
"properties_from_tool",
"helical",
QT_TRANSLATE_NOOP(
"App::Property",
"if helix_angle is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear",
),
)
obj.addProperty(
"App::PropertyAngle",
"helix_angle",
"helical",
QT_TRANSLATE_NOOP("App::Property", "helix angle"),
)
obj.addProperty(
"App::PropertyBool",
"double_helix",
"helical",
QT_TRANSLATE_NOOP("App::Property", "double helix"),
)
def add_computed_properties(self, obj):
obj.addProperty(
"App::PropertyLength",
"transverse_pitch",
"computed",
QT_TRANSLATE_NOOP("App::Property", "pitch in the transverse plane"),
1,
)
obj.addProperty(
"App::PropertyBool",
"add_endings",
"base",
QT_TRANSLATE_NOOP(
"App::Property",
"if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank",
),
)
def add_tolerance_properties(self, obj):
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "head * module = additional length of head"
),
)
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "clearance * module = additional length of root"
),
)
def add_involute_properties(self, obj):
obj.addProperty(
"App::PropertyAngle",
"pressure_angle",
"involute",
QT_TRANSLATE_NOOP("App::Property", "pressure angle"),
)
def add_fillet_properties(self, obj):
obj.addProperty(
"App::PropertyFloatConstraint",
"head_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-head, radius = head_fillet x module",
),
).head_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyFloatConstraint",
"root_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-root, radius = root_fillet x module",
),
).root_fillet = (0.0, 0.0, 1000.0, 0.01)
def generate_gear_shape(self, obj):
obj.rack.m = obj.module.Value
obj.rack.z = obj.num_teeth
obj.rack.pressure_angle = obj.pressure_angle.Value * np.pi / 180.0
obj.rack.thickness = obj.thickness.Value
obj.rack.beta = obj.helix_angle.Value * np.pi / 180.0
obj.rack.head = obj.head
# checksbackwardcompatibility:
if "clearance" in obj.PropertiesList:
obj.rack.clearance = obj.clearance
if "properties_from_tool" in obj.PropertiesList:
obj.rack.properties_from_tool = obj.properties_from_tool
if "add_endings" in obj.PropertiesList:
obj.rack.add_endings = obj.add_endings
if "simplified" in obj.PropertiesList:
obj.rack.simplified = obj.simplified
obj.rack._update()
m, m_n, pitch, pressure_angle_t = obj.rack.compute_properties()
obj.transverse_pitch = "{} mm".format(pitch)
t = obj.thickness.Value
c = obj.clearance
h = obj.head
alpha = obj.pressure_angle.Value * np.pi / 180.0
head_fillet = obj.head_fillet
root_fillet = obj.root_fillet
x1 = -m * np.pi / 2
y1 = -m * (1 + c)
y2 = y1
x2 = -m * np.pi / 4 + y2 * np.tan(alpha)
y3 = m * (1 + h)
x3 = -m * np.pi / 4 + y3 * np.tan(alpha)
x4 = -x3
x5 = -x2
x6 = -x1
y4 = y3
y5 = y2
y6 = y1
p1 = np.array([y1, x1])
p2 = np.array([y2, x2])
p3 = np.array([y3, x3])
p4 = np.array([y4, x4])
p5 = np.array([y5, x5])
p6 = np.array([y6, x6])
line1 = [p1, p2]
line2 = [p2, p3]
line3 = [p3, p4]
line4 = [p4, p5]
line5 = [p5, p6]
tooth = part.Wire(points_to_wire([line1, line2, line3, line4, line5]))
edges = tooth.Edges
edges = insert_fillet(edges, 0, m * root_fillet)
edges = insert_fillet(edges, 2, m * head_fillet)
edges = insert_fillet(edges, 4, m * head_fillet)
edges = insert_fillet(edges, 6, m * root_fillet)
tooth_edges = [e for e in edges if e is not None]
p_end = np.array(tooth_edges[-2].lastVertex().Point[:-1])
p_start = np.array(tooth_edges[1].firstVertex().Point[:-1])
p_start += np.array([0, np.pi * m])
edge = points_to_wire([[p_end, p_start]]).Edges
tooth = part.Wire(tooth_edges[1:-1] + edge)
teeth = [tooth]
for i in range(obj.num_teeth - 1):
tooth = tooth.copy()
tooth.translate(app.Vector(0, np.pi * m, 0))
teeth.append(tooth)
teeth[-1] = part.Wire(teeth[-1].Edges[:-1])
if obj.add_endings:
teeth = [part.Wire(tooth_edges[0])] + teeth
last_edge = tooth_edges[-1]
last_edge.translate(app.Vector(0, np.pi * m * (obj.num_teeth - 1), 0))
teeth = teeth + [part.Wire(last_edge)]
p_start = np.array(teeth[0].Edges[0].firstVertex().Point[:-1])
p_end = np.array(teeth[-1].Edges[-1].lastVertex().Point[:-1])
p_start_1 = p_start - np.array([obj.thickness.Value, 0.0])
p_end_1 = p_end - np.array([obj.thickness.Value, 0.0])
line6 = [p_start, p_start_1]
line7 = [p_start_1, p_end_1]
line8 = [p_end_1, p_end]
bottom = points_to_wire([line6, line7, line8])
pol = part.Wire([bottom] + teeth)
if obj.height.Value == 0:
return pol
elif obj.rack.beta == 0:
face = part.Face(part.Wire(pol))
return face.extrude(fcvec([0.0, 0.0, obj.height.Value]))
elif obj.double_helix:
pol2 = part.Wire(pol)
pol2.translate(
fcvec([0.0, np.tan(obj.rack.beta) * obj.height.Value / 2, obj.height.Value / 2])
)
pol3 = part.Wire(pol)
pol3.translate(fcvec([0.0, 0.0, obj.height.Value]))
return part.makeLoft([pol, pol2, pol3], True, True)
else:
pol2 = part.Wire(pol)
pol2.translate(
fcvec([0.0, np.tan(obj.rack.beta) * obj.height.Value, obj.height.Value])
)
return part.makeLoft([pol, pol2], True)

View File

@@ -0,0 +1,150 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
import scipy as sp
from freecad import app
from freecad import part
from pygears.bevel_tooth import BevelTooth
from pygears._functions import rotation
from .basegear import BaseGear, fcvec, part_arc_from_points_and_center
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class LanternGear(BaseGear):
def __init__(self, obj):
super(LanternGear, self).__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"gear_parameter",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP("App::Property", "module"),
)
obj.addProperty(
"App::PropertyLength",
"bolt_radius",
"base",
QT_TRANSLATE_NOOP("App::Property", "the bolt radius of the rack/chain"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyInteger",
"num_profiles",
"accuracy",
QT_TRANSLATE_NOOP("App::Property", "number of profiles used for loft"),
)
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "head * module = additional length of head"
),
)
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.module = "1. mm"
obj.bolt_radius = "1 mm"
obj.height = "5. mm"
obj.num_profiles = 10
self.obj = obj
obj.Proxy = self
def generate_gear_shape(self, fp):
m = fp.module.Value
teeth = fp.num_teeth
r_r = fp.bolt_radius.Value
r_0 = m * teeth / 2
r_max = r_0 + r_r + fp.head * m
phi_max = (r_r + np.sqrt(r_max**2 - r_0**2)) / r_0
def find_phi_min(phi_min):
return r_0 * (
phi_min**2 * r_0
- 2 * phi_min * r_0 * np.sin(phi_min)
- 2 * phi_min * r_r
- 2 * r_0 * np.cos(phi_min)
+ 2 * r_0
+ 2 * r_r * np.sin(phi_min)
)
phi_min = sp.optimize.root(find_phi_min, (phi_max + r_r / r_0 * 4) / 5).x[
0
] # , r_r / r_0, phi_max)
# phi_min = 0 # r_r / r_0
phi = np.linspace(phi_min, phi_max, fp.num_profiles)
x = r_0 * (np.cos(phi) + phi * np.sin(phi)) - r_r * np.sin(phi)
y = r_0 * (np.sin(phi) - phi * np.cos(phi)) + r_r * np.cos(phi)
xy1 = np.array([x, y]).T
p_1 = xy1[0]
p_1_end = xy1[-1]
bsp_1 = part.BSplineCurve()
bsp_1.interpolate(list(map(fcvec, xy1)))
w_1 = bsp_1.toShape()
xy2 = xy1 * np.array([1.0, -1.0])
p_2 = xy2[0]
p_2_end = xy2[-1]
bsp_2 = part.BSplineCurve()
bsp_2.interpolate(list(map(fcvec, xy2)))
w_2 = bsp_2.toShape()
p_12 = np.array([r_0 - r_r, 0.0])
arc = part.Arc(
app.Vector(*p_1, 0.0), app.Vector(*p_12, 0.0), app.Vector(*p_2, 0.0)
).toShape()
rot = rotation(np.pi * 2 / teeth)
p_3 = rot(np.array([p_2_end]))[0]
# l = part.LineSegment(fcvec(p_1_end), fcvec(p_3)).toShape()
l = part_arc_from_points_and_center(
p_1_end, p_3, np.array([0.0, 0.0])
).toShape()
w = part.Wire([w_2, arc, w_1, l])
wires = [w]
rot = app.Matrix()
for _ in range(teeth - 1):
rot.rotateZ(np.pi * 2 / teeth)
wires.append(w.transformGeometry(rot))
wi = part.Wire(wires)
if fp.height.Value == 0:
return wi
else:
return part.Face(wi).extrude(app.Vector(0, 0, fp.height))

View File

@@ -1,113 +0,0 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
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))

325
freecad/gears/timinggear.py Normal file
View File

@@ -0,0 +1,325 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from freecad import part
from pygears._functions import reflection
from .basegear import BaseGear, part_arc_from_points_and_center
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class TimingGear(BaseGear):
"""FreeCAD gear rack"""
data = {
"gt2": {
"pitch": 2.0,
"u": 0.254,
"h": 0.75,
"H": 1.38,
"r0": 0.555,
"r1": 1.0,
"rs": 0.15,
"offset": 0.40,
},
"gt3": {
"pitch": 3.0,
"u": 0.381,
"h": 1.14,
"H": 2.40,
"r0": 0.85,
"r1": 1.52,
"rs": 0.25,
"offset": 0.61,
},
"gt5": {
"pitch": 5.0,
"u": 0.5715,
"h": 1.93,
"H": 3.81,
"r0": 1.44,
"r1": 2.57,
"rs": 0.416,
"offset": 1.03,
},
"gt8": {
"pitch": 8.0,
"u": 0.9144,
"h": 3.088,
"H": 6.096,
"r0": 2.304,
"r1": 4.112,
"rs": 0.6656,
"offset": 1.648,
},
"htd3": {
"pitch": 3.0,
"u": 0.381,
"h": 1.21,
"H": 2.40,
"r0": 0.89,
"r1": 0.89,
"rs": 0.26,
"offset": 0.0,
},
"htd5": {
"pitch": 5.0,
"u": 0.5715,
"h": 2.06,
"H": 3.80,
"r0": 1.49,
"r1": 1.49,
"rs": 0.43,
"offset": 0.0,
},
"htd8": {
"pitch": 8.0,
"u": 0.686,
"h": 3.45,
"H": 6.00,
"r0": 2.46,
"r1": 2.46,
"rs": 0.70,
"offset": 0.0,
},
}
def __init__(self, obj):
super(TimingGear, self).__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyEnumeration",
"type",
"base",
QT_TRANSLATE_NOOP("App::Property", "type of timing-gear"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyLength",
"pitch",
"computed",
QT_TRANSLATE_NOOP("App::Property", "pitch of gear"),
1,
)
obj.addProperty(
"App::PropertyLength",
"h",
"computed",
QT_TRANSLATE_NOOP("App::Property", "radial height of teeth"),
1,
)
obj.addProperty(
"App::PropertyLength",
"u",
"computed",
QT_TRANSLATE_NOOP(
"App::Property",
"radial difference between pitch diameter and head of gear",
),
1,
)
obj.addProperty(
"App::PropertyLength",
"r0",
"computed",
QT_TRANSLATE_NOOP("App::Property", "radius of first arc"),
1,
)
obj.addProperty(
"App::PropertyLength",
"r1",
"computed",
QT_TRANSLATE_NOOP("App::Property", "radius of second arc"),
1,
)
obj.addProperty(
"App::PropertyLength",
"rs",
"computed",
QT_TRANSLATE_NOOP("App::Property", "radius of third arc"),
1,
)
obj.addProperty(
"App::PropertyLength",
"offset",
"computed",
QT_TRANSLATE_NOOP("App::Property", "x-offset of second arc-midpoint"),
1,
)
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.type = ["gt2", "gt3", "gt5", "gt8", "htd3", "htd5", "htd8"]
obj.height = "5. mm"
self.obj = obj
obj.Proxy = self
def generate_gear_shape(self, fp):
# m ... center of arc/circle
# r ... radius of arc/circle
# x ... end-point of arc
# phi ... angle
tp = fp.type
gt_data = self.data[tp]
pitch = fp.pitch = gt_data["pitch"]
h = fp.h = gt_data["h"]
u = fp.u = gt_data["u"]
r_12 = fp.r0 = gt_data["r0"]
r_23 = fp.r1 = gt_data["r1"]
r_34 = fp.rs = gt_data["rs"]
offset = fp.offset = gt_data["offset"]
arcs = []
if offset == 0.0:
phi5 = np.pi / fp.num_teeth
ref = reflection(-phi5 - np.pi / 2.0)
rp = pitch * fp.num_teeth / np.pi / 2.0 - u
m_34 = np.array([-(r_12 + r_34), rp - h + r_12])
x2 = np.array([-r_12, m_34[1]])
x4 = np.array([m_34[0], m_34[1] + r_34])
x6 = ref(x4)
mir = np.array([-1.0, 1.0])
xn2 = mir * x2
xn4 = mir * x4
mn_34 = mir * m_34
arcs.append(part_arc_from_points_and_center(xn4, xn2, mn_34).toShape())
arcs.append(
part.Arc(
app.Vector(*xn2, 0.0),
app.Vector(0, rp - h, 0.0),
app.Vector(*x2, 0.0),
).toShape()
)
arcs.append(part_arc_from_points_and_center(x2, x4, m_34).toShape())
arcs.append(
part_arc_from_points_and_center(x4, x6, np.array([0.0, 0.0])).toShape()
)
else:
phi_12 = np.arctan(np.sqrt(1.0 / (((r_12 - r_23) / offset) ** 2 - 1)))
rp = pitch * fp.num_teeth / np.pi / 2.0
r4 = r5 = rp - u
m_12 = np.array([0.0, r5 - h + r_12])
m_23 = np.array([offset, offset / np.tan(phi_12) + m_12[1]])
m_23y = m_23[1]
# solving for phi4:
# sympy.solve(
# ((r5 - r_34) * sin(phi4) + offset) ** 2 + \
# ((r5 - r_34) * cos(phi4) - m_23y) ** 2 - \
# ((r_34 + r_23) ** 2), phi4)
phi4 = 2 * np.arctan(
(
-2 * offset * r5
+ 2 * offset * r_34
+ np.sqrt(
-(m_23y**4)
- 2 * m_23y**2 * offset**2
+ 2 * m_23y**2 * r5**2
- 4 * m_23y**2 * r5 * r_34
+ 2 * m_23y**2 * r_23**2
+ 4 * m_23y**2 * r_23 * r_34
+ 4 * m_23y**2 * r_34**2
- offset**4
+ 2 * offset**2 * r5**2
- 4 * offset**2 * r5 * r_34
+ 2 * offset**2 * r_23**2
+ 4 * offset**2 * r_23 * r_34
+ 4 * offset**2 * r_34**2
- r5**4
+ 4 * r5**3 * r_34
+ 2 * r5**2 * r_23**2
+ 4 * r5**2 * r_23 * r_34
- 4 * r5**2 * r_34**2
- 4 * r5 * r_23**2 * r_34
- 8 * r5 * r_23 * r_34**2
- r_23**4
- 4 * r_23**3 * r_34
- 4 * r_23**2 * r_34**2
)
)
/ (
m_23y**2
+ 2 * m_23y * r5
- 2 * m_23y * r_34
+ offset**2
+ r5**2
- 2 * r5 * r_34
- r_23**2
- 2 * r_23 * r_34
)
)
phi5 = np.pi / fp.num_teeth
m_34 = (r5 - r_34) * np.array([-np.sin(phi4), np.cos(phi4)])
x2 = np.array([-r_12 * np.sin(phi_12), m_12[1] - r_12 * np.cos(phi_12)])
x3 = m_34 + r_34 / (r_34 + r_23) * (m_23 - m_34)
x4 = r4 * np.array([-np.sin(phi4), np.cos(phi4)])
ref = reflection(-phi5 - np.pi / 2)
x6 = ref(x4)
mir = np.array([-1.0, 1.0])
xn2 = mir * x2
xn3 = mir * x3
xn4 = mir * x4
mn_34 = mir * m_34
mn_23 = mir * m_23
arcs.append(part_arc_from_points_and_center(xn4, xn3, mn_34).toShape())
arcs.append(part_arc_from_points_and_center(xn3, xn2, mn_23).toShape())
arcs.append(part_arc_from_points_and_center(xn2, x2, m_12).toShape())
arcs.append(part_arc_from_points_and_center(x2, x3, m_23).toShape())
arcs.append(part_arc_from_points_and_center(x3, x4, m_34).toShape())
arcs.append(
part_arc_from_points_and_center(x4, x6, np.array([0.0, 0.0])).toShape()
)
wire = part.Wire(arcs)
wires = [wire]
rot = app.Matrix()
rot.rotateZ(np.pi * 2 / fp.num_teeth)
for _ in range(fp.num_teeth - 1):
wire = wire.transformGeometry(rot)
wires.append(wire)
wi = part.Wire(wires)
if fp.height.Value == 0:
return wi
else:
return part.Face(wi).extrude(app.Vector(0, 0, fp.height))

View File

@@ -0,0 +1,191 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
import scipy as sp
from scipy import optimize
from freecad import app
from freecad import part
from pygears._functions import rotation, reflection
from .basegear import BaseGear, fcvec, part_arc_from_points_and_center, insert_fillet
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class TimingGearT(BaseGear):
def __init__(self, obj):
obj.addProperty(
"App::PropertyLength",
"pitch",
"base",
QT_TRANSLATE_NOOP("App::Property", "pitch of gear"),
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"tooth_height",
"base",
QT_TRANSLATE_NOOP("App::Property", "radial height of tooth"),
)
obj.addProperty(
"App::PropertyLength",
"u",
"base",
QT_TRANSLATE_NOOP(
"App::Property", "radial distance from tooth-head to pitch circle"
),
)
obj.addProperty(
"App::PropertyLength",
"backlash",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property",
"The arc length on the pitch circle by which the tooth thickness is reduced.",
),
)
obj.addProperty(
"App::PropertyFloatConstraint",
"head_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-head, radius = head_fillet x module",
),
).head_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyFloatConstraint",
"root_fillet",
"fillets",
QT_TRANSLATE_NOOP(
"App::Property",
"a fillet for the tooth-root, radius = root_fillet x module",
),
).root_fillet = (0.0, 0.0, 1000.0, 0.01)
obj.addProperty(
"App::PropertyAngle",
"alpha",
"base",
QT_TRANSLATE_NOOP("App::Property", "angle of tooth flanks"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "extrusion height"),
)
obj.pitch = "5. mm"
obj.num_teeth = (15, 3, 10000, 1) # default, min, max, step
obj.tooth_height = "1.2 mm"
obj.u = "0.6 mm"
obj.alpha = "40. deg"
obj.height = "5 mm"
obj.backlash = "0. mm"
obj.head_fillet = 0.4
obj.root_fillet = 0.4
self.obj = obj
obj.Proxy = self
def generate_gear_shape(self, obj):
pitch = obj.pitch.Value
teeth = obj.num_teeth
u = obj.u.Value
tooth_height = obj.tooth_height.Value
alpha = obj.alpha.Value / 180.0 * np.pi # we need radiant
height = obj.height.Value
backlash = obj.backlash.Value
head_fillet = obj.head_fillet
root_fillet = obj.root_fillet
r_p = pitch * teeth / 2.0 / np.pi
gamma_0 = pitch / r_p
gamma_backlash = backlash / r_p
gamma_1 = gamma_0 / 4 - gamma_backlash
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.0).x
s2 = sp.optimize.minimize(dist_p2, 0.0).x
p_1 = line(s1)
p_2 = line(s2)
mirror = reflection(0.0) # reflect the points at the x-axis
p_3, p_4 = mirror(np.array([p_2, p_1]))
# for the fillets we need some more points
rot = rotation(gamma_0)
p_5, p_6, p_7 = rot(
np.array([p_1, p_2, p_3])
) # the rotation expects a list of points
e1 = part.LineSegment(fcvec(p_1), fcvec(p_2)).toShape()
e2 = part_arc_from_points_and_center(p_2, p_3, np.array([0.0, 0.0])).toShape()
e3 = part.LineSegment(fcvec(p_3), fcvec(p_4)).toShape()
e4 = part_arc_from_points_and_center(p_4, p_5, np.array([0.0, 0.0])).toShape()
e5 = part.LineSegment(fcvec(p_5), fcvec(p_6)).toShape()
e6 = part_arc_from_points_and_center(p_6, p_7, np.array([0.0, 0.0])).toShape()
edges = [e1, e2, e3, e4, e5, e6]
edges = insert_fillet(edges, 4, head_fillet)
# somehow we need to reverse the normal here
edges = insert_fillet(edges, 3, root_fillet, reversed=True)
edges = insert_fillet(edges, 2, root_fillet, reversed=True)
edges = insert_fillet(edges, 1, head_fillet)
edges = insert_fillet(edges, 0, head_fillet)
edges = edges[2:-1]
edges = [edge for edge in edges if edge is not None]
w = part.Wire(edges)
# 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, 0.0, height))

View File

@@ -0,0 +1,712 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@@ -0,0 +1,718 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de" sourcelanguage="en_US">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation type="unfinished">freecad.gears-version</translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation>Zähnezahl</translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation type="unfinished">height</translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation type="unfinished">pitch_angle</translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation>Eingriffwinkel</translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation>Modul</translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation type="unfinished">clearance</translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translation type="unfinished">number of points for spline</translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation type="unfinished">if value is true the gears outer face will match the z=0 plane</translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation type="unfinished">The arc length on the pitch circle by which the tooth thicknes is reduced.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation type="unfinished">test</translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation type="unfinished">angle used for spiral bevel-gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation>Der Teilkreisdurchmesser.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation type="unfinished">The angle by which this gear can turn without moving the mating gear.</translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation type="unfinished">master gear</translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation type="unfinished">slave gear</translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation type="unfinished">angle at which second gear is placed</translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation>Zähnezahl des anderen Zahnrades</translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation type="unfinished">thickness</translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation>Eingriffwinkel</translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation type="unfinished">number of profiles used for loft</translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation type="unfinished">if true no boolean operation is done</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation>das Python-Objekt</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation>Doppelwendel</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation>beta</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation>eine Abrundung des Zahnkopfes, radius = head_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation>eine Abrundung des Zahngrundes, radius = head_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation type="unfinished">head_value * module_value = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation type="unfinished">inner_diameter divided by module (hypocycloid)</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation type="unfinished">outer_diameter divided by module (epicycloid)</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation type="unfinished">if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation type="unfinished">pitch in the transverse plane</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation type="unfinished">if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation type="unfinished">head * module = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation type="unfinished">clearance * module = additional length of root</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation type="unfinished">Pin ball circle radius (overrides Tooth Pitch)</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation>Rollendurchmesser</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation type="unfinished">Eccentricity</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation type="unfinished">Pressure angle limit</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation type="unfinished">Offset in pressure angle</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation type="unfinished">Number of teeth in Cam</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation type="unfinished">Number of points used for spline interpolation</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation type="unfinished">Center hole&apos;s radius</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation type="unfinished">Create pins in place</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation type="unfinished">Center pin Z axis to generated disks</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation type="unfinished">Show main cam disk</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation type="unfinished">Show another reversed cam disk on top</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation type="unfinished">simple</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation type="unfinished">normal module if properties_from_tool=True, else it&apos;s the transverse module.</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation>Innendurchmesser</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation type="unfinished">root diameter</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation type="unfinished">transverse_pitch</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation>Außendurchmesser</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation type="unfinished">backlash direction</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation type="unfinished">shift</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation type="unfinished">if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation type="unfinished">python gear object</translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation>Hinterschnitt</translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation>Außendurchmesser</translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation type="unfinished">traverse module of the generated gear</translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation>der Rollenradius der Zahnstange bzw. Kette</translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation>Art der Zahnriemenscheibe</translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation type="unfinished">pitch of gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation type="unfinished">radial height of teeth</translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation type="unfinished">radial difference between pitch diameter and head of gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation type="unfinished">radius of first arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation type="unfinished">radius of second arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation type="unfinished">radius of third arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation type="unfinished">x-offset of second arc-midpoint</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation type="unfinished">radial height of tooth</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation type="unfinished">radial distance from tooth-head to pitch circle</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation type="unfinished">angle of tooth flanks</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation type="unfinished">extrusion height</translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation>Durchmesser</translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation type="unfinished">reverse rotation of helix</translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation>Kegelrad</translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation>Erstellt ein Kegelrad</translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation>Kronenrad</translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation>Erstellt ein Kronenrad</translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation>Zykloidenrad</translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation>Erstellt ein Zykloidenrad</translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation>Zykloidenzahnstange</translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation>Erstellt eine Zykloidenzahnstange</translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation type="unfinished">Combine two gears</translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation>Hypozykloidenrad</translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation>Erstellt ein Paar Hypozykloidenräder mit zugehörigen Rollen</translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation>Innenverzahntes Evolventenrad</translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation>Erstellt ein innenverzahntes Evolventenrad</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation>Evolventenrad</translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation>Erstellt ein Evolventenstirnrad</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation>Evolventenzahnstange</translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation>Erstellt eine Evolventenzahnstange</translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation>Triebstockrad</translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation>Erstellt ein Triebstockrad</translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation>Zahnriemenscheibe</translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation>Erstellt eine Zahnriemenscheibe</translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation>ZahnriemenscheibeT</translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation>Erstellt eine Zahnriemenscheibe Bauart T</translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation>Schneckenwelle</translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation>Erstellt eine Schneckenwelle</translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation>Bitte zwei Zahnradobjekte auswählen.</translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation>Das ausgewählte Objekt ist kein Zahnrad.</translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation>Gear-Modul: Kronenrad erstellt, preview_mode = true für bessere Leistung. Eigenschaft preview_mode property auf false setzen, wenn Zähne dargestellt werden sollen.</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation type="unfinished">Generating cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation type="unfinished">Generating secondary cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation type="unfinished">Generating pins
</translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation type="unfinished">Checking FreeCAD version
</translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation>FreeCAD-Version (derzeit {}.{}.{} ({})) muss mindestens {}.{}.{} ({}) sein, um mit Python 3.11 und neuer zu funktionieren
</translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation type="unfinished">Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation>Gear</translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation>Arbeitsbereich Gear</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@@ -0,0 +1,718 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="el" sourcelanguage="en_US">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation>freecad.γρανάζια-έκδοση</translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation>αριθμός δοντιών</translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation>ύψος</translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation>βημα_γωγίας</translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation>πίεση_γωνίας</translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation type="unfinished">module</translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation>ελευθέρωση</translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translation>αριθμός σημείων για λωρίδα</translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation>Εάν η τιμή είναι true (αληθής), η εξωτερική όψη των γραναζιών θα ταιριάζει με το επίπεδο z=0</translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation type="unfinished">The arc length on the pitch circle by which the tooth thicknes is reduced.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation type="unfinished">test</translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation type="unfinished">angle used for spiral bevel-gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation type="unfinished">The pitch diameter.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation type="unfinished">The angle by which this gear can turn without moving the mating gear.</translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation type="unfinished">master gear</translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation type="unfinished">slave gear</translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation type="unfinished">angle at which second gear is placed</translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation type="unfinished">number of teeth of other gear</translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation type="unfinished">thickness</translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation type="unfinished">pressure angle</translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation type="unfinished">number of profiles used for loft</translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation type="unfinished">if true no boolean operation is done</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation type="unfinished">the python object</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation type="unfinished">double helix</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation type="unfinished">beta</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation type="unfinished">a fillet for the tooth-head, radius = head_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation type="unfinished">a fillet for the tooth-root, radius = root_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation type="unfinished">head_value * module_value = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation type="unfinished">inner_diameter divided by module (hypocycloid)</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation type="unfinished">outer_diameter divided by module (epicycloid)</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation type="unfinished">if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation type="unfinished">pitch in the transverse plane</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation type="unfinished">if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation type="unfinished">head * module = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation type="unfinished">clearance * module = additional length of root</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation type="unfinished">Pin ball circle radius (overrides Tooth Pitch)</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation type="unfinished">Roller Diameter</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation type="unfinished">Eccentricity</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation type="unfinished">Pressure angle limit</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation type="unfinished">Offset in pressure angle</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation type="unfinished">Number of teeth in Cam</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation type="unfinished">Number of points used for spline interpolation</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation type="unfinished">Center hole&apos;s radius</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation type="unfinished">Create pins in place</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation type="unfinished">Center pin Z axis to generated disks</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation type="unfinished">Show main cam disk</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation type="unfinished">Show another reversed cam disk on top</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation>απλό</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation type="unfinished">normal module if properties_from_tool=True, else it&apos;s the transverse module.</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation>εσωτερική διάμετρος</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation type="unfinished">root diameter</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation type="unfinished">transverse_pitch</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation>Εξωτερική διάμετρος</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation type="unfinished">backlash direction</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation type="unfinished">shift</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation type="unfinished">if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation type="unfinished">python gear object</translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation type="unfinished">undercut</translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation type="unfinished">outside diameter</translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation type="unfinished">traverse module of the generated gear</translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation>Η ακτίνα του μπουλονιού της οδοντωτής ράβδος/αλυσίδας</translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation>τύπος μηχανισμού χρονισμού</translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation>βήμα γραναζιών</translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation type="unfinished">radial height of teeth</translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation type="unfinished">radial difference between pitch diameter and head of gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation type="unfinished">radius of first arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation>ακτίνα δεύτερου τόξου</translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation type="unfinished">radius of third arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation type="unfinished">x-offset of second arc-midpoint</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation type="unfinished">radial height of tooth</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation type="unfinished">radial distance from tooth-head to pitch circle</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation type="unfinished">angle of tooth flanks</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation type="unfinished">extrusion height</translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation type="unfinished">diameter</translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation type="unfinished">reverse rotation of helix</translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation type="unfinished">Bevel Gear</translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation type="unfinished">Create a Bevel gear</translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation type="unfinished">Crown Gear</translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation type="unfinished">Create a Crown gear</translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation type="unfinished">Cycloid Gear</translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation type="unfinished">Create a Cycloid gear</translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation type="unfinished">Cycloid Rack</translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation type="unfinished">Create an Cycloid rack</translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation type="unfinished">Combine two gears</translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation type="unfinished">HypoCycloid Gear</translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation type="unfinished">Create a HypoCycloid gear with its pins</translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation>Εσωτερικό Ελικοειδές Γρανάζι</translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation>Δημιουργήστε ένα εσωτερικό ελικοειδές γρανάζι</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation>Ελικοειδές Γρανάζια</translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation>Δημιουργήστε ένα εξωτερικό ελικοειδές γρανάζι</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation>Ελικοειδές Οδοντωτή ράβδος</translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation>Δημιουργήστε μαι Ελικοειδή Οδοντωτή ράβδος</translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation type="unfinished">Lantern Gear</translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation type="unfinished">Create a Lantern gear</translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation type="unfinished">Timing Gear</translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation type="unfinished">Create a Timing gear</translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation type="unfinished">Timing Gear T-shape</translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation type="unfinished">Create a Timing gear T-shape</translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation type="unfinished">Worm Gear</translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation type="unfinished">Create a Worm gear</translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation type="unfinished">Please select two gear objects.</translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation type="unfinished">Selected object is not a gear.</translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation type="unfinished">Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation type="unfinished">Generating cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation type="unfinished">Generating secondary cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation type="unfinished">Generating pins
</translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation type="unfinished">Checking FreeCAD version
</translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation>Η έκδοση FreeCAD (προς το παρόν {}.{}.{} ({})) πρέπει να είναι τουλάχιστον {}.{}.{} ({}) για να λειτουργήσει με Python 3.11 και νεότερη έκδοση
</translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation type="unfinished">Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation>Γρανάζι</translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation>Πάγκος εργασίας Γραναζιών</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@@ -0,0 +1,719 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="es_AR" sourcelanguage="en_US">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation>Versión de freecad.gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation>Número de dientes</translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation>Altura</translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation>Ángulo de paso</translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation>Ángulo de presión</translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation>Módulo</translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation>Margen</translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translatorcomment>En la página wiki en español se mantiene la palabra &quot;spline&quot;: https://es.wikipedia.org/wiki/Spline</translatorcomment>
<translation>Número de puntos para la spline</translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation>Si el valor es verdadero la cara exterior del engranaje va a coincidir con el plano z=0</translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation>La longitud de arco en el círculo de paso en la que se reduce el grosor del diente.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation>Prueba</translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation>Ángulo utilizado para engranajes cónicos en espiral</translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation>El diámetro de paso.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation>El ángulo que puede girar este engranaje sin mover el engranaje de acoplamiento.</translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation>Engranaje maestro</translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation>Engranaje esclavo</translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation>Ángulo al cual el segundo engrane es colocado</translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation>Número de dientes del otro engranaje</translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation>Grosor</translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation>Ángulo de presión</translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation>Número de perfiles usados en la proyección</translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation>Si es verdadero ninguna operación booleana es realizada</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation>El objeto Python</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation>Hélice doble</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation>Beta</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation>Un redondeo para la cabeza del diente, radio = redondeo_cabeza x módulo</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation>Un redondeo para la raíz del diente, radio = redondeo_raíz x módulo</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation>valor_cabeza * valor_módulo = longitud adicional de la cabeza</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation>Diámetro interno dividido por el módulo (hipocicloide)</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation>Diámetro externo dividido por el módulo (epicicloide)</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation>Si está activada, la cremallera se dibuja con un número constante de dientes para evitar el renombrado topológico.</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation>Paso en el plano transversal</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation>Si está activada, la longitud total de la cremallera es dientes x paso, en caso contrario la cremallera comienza con un flanco de diente</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation>cabeza * módulo = longitud adicional de la cabeza</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation>margen * módulo = longitud adicional de la raíz</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation>Radio del círculo de la bola del pasador (anula el paso del diente)</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation>Diámetro de rodillo</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation>Excentricidad</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation>Límite de ángulo de presión</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation>Desplazamiento en el ángulo de presión</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation>Número de dientes en la leva</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation>Número de puntos usados en la interpolación de la spline</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation>Radio del agujero central</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation>Crear pasadores en el lugar</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation>Centrar el eje Z del pasador a los discos generados</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation>Mostrar disco de levas principal</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation>Mostrar otro disco de leva invertido en la parte superior</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation>Simple</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation>Módulo normal si properties_from_tool es verdadero, en caso contrario es el módulo transversal.</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation>Diámetro interior</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation>Diámetro raíz</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation>Paso transversal</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation>Diámetro exterior</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation>Dirección de retroceso</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation>Desplazamiento</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation>Si se indica beta y se activa properties_from_tool, los parámetros del engranaje se vuelven a calcular internamente para el engranaje girado</translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation>Objeto engranaje Python</translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation>Socavado</translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation>Diámetro exterior</translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation>Módulo transversal del engranaje generado</translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation>El radio del perno de la cremallera/cadena</translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation>Tipo de piñon de sincronización</translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation>Paso del engranaje</translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation>Altura radial del diente</translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation>diferencia radial entre el diámetro de paso y la cabeza del engranaje</translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation>Radio del primer arco</translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation>Radio del segundo arco</translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation>Radios del tercer radio</translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation>Desplazamiento en x del segundo punto medio del arco</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation>Altura radial del diente</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation>Distancia radial desde la cabeza del diente a círculo de paso</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation>Ángulo de los flancos de los dientes</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation>Altura de extrusión</translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation>Diámetro</translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation>Revertir rotación de hélice</translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation>Engranaje cónico</translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation>Crear un engranaje cónico</translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation>Engranaje de corona</translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation>Crear un engranaje de corona</translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation>Engranaje cicloide</translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation>Crear un engranaje cicloide</translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation>Cremallera cicloide</translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation>Crear una cremallera cicloide</translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation>Combinar dos engranajes</translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation>Engranaje hipocicloide</translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation>Crear un engranaje hipocicloide con sus pasadores</translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation>Engranaje evolvente interno</translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation>Crear un engranaje evolvente interno</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation>Engranaje evolvente</translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation>Crear un engranaje evolvente externo</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation>Cremallera evolvente</translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation>Crear un cremallera evolvente</translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation>Piñon de linterna</translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation>Crear un piñon de linterna</translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation>Piñon de sincronización</translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation>Crear un piñon de sincronización</translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation>Piñon de sincronización con forma de T</translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation>Crear un piñon de sincronización con forma de T</translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation>Tornillo sin fin</translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation>Crear un tornillo sin fin</translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation>Por favor seleccione dos objetos de tipo engranaje.</translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation>El objeto seleccionado no es un engranaje.</translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation>Módulo Gear: Engranaje de corona creado, preview_mode=true para un rendimiento mejorado. Establezca la propiedad preview_mode en falso cuando esté listo para cortar los dientes.</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation>Generando disco de leva
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation>Generando disco de leva secundario
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation>Generando pasadores
</translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation>Comprobando versión de FreeCAD
</translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation>La versión de FreeCAD (actualmente {}.{}.{} ({})) debe de ser por lo menos {}.{}.{} ({}) para poder trabajar con Python 3.11 y versiones superiores
</translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation>Migrando propiedad de &apos;teeth&apos; a &apos;num_teeth&apos; en la parte {}
</translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation>Engranajes</translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation>Banco de trabajo Engranajes</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@@ -0,0 +1,719 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="es_ES" sourcelanguage="en_US">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation>Versión de freecad.gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation>Número de dientes</translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation>Altura</translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation>Ángulo de paso</translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation>Ángulo de presión</translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation>Módulo</translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation>Margen</translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translatorcomment>En la página wiki en español se mantiene la palabra &quot;spline&quot;: https://es.wikipedia.org/wiki/Spline</translatorcomment>
<translation>Número de puntos para la spline</translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation>Si el valor es verdadero la cara exterior del engranaje va a coincidir con el plano z=0</translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation>La longitud de arco en el círculo de paso en la que se reduce el grosor del diente.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation>Prueba</translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation>Ángulo utilizado para engranajes cónicos en espiral</translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation>El diámetro de paso.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation>El ángulo que puede girar este engranaje sin mover el engranaje de acoplamiento.</translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation>Engranaje maestro</translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation>Engranaje esclavo</translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation>Ángulo al cual el segundo engrane es colocado</translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation>Número de dientes del otro engranaje</translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation>Grosor</translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation>Ángulo de presión</translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation>Número de perfiles usados en la proyección</translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation>Si es verdadero ninguna operación booleana es realizada</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation>El objeto Python</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation>Hélice doble</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation>Beta</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation>Un redondeo para la cabeza del diente, radio = redondeo_cabeza x módulo</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation>Un redondeo para la raíz del diente, radio = redondeo_raíz x módulo</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation>valor_cabeza * valor_módulo = longitud adicional de la cabeza</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation>Diámetro interno dividido por el módulo (hipocicloide)</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation>Diámetro externo dividido por el módulo (epicicloide)</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation>Si está activada, la cremallera se dibuja con un número constante de dientes para evitar el renombrado topológico.</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation>Paso en el plano transversal</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation>Si está activada, la longitud total de la cremallera es dientes x paso, en caso contrario la cremallera comienza con un flanco de diente</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation>cabeza * módulo = longitud adicional de la cabeza</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation>margen * módulo = longitud adicional de la raíz</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation>Radio del círculo de la bola del pasador (anula el paso del diente)</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation>Diámetro de rodillo</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation>Excentricidad</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation>Límite de ángulo de presión</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation>Desplazamiento en el ángulo de presión</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation>Número de dientes en la leva</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation>Número de puntos usados en la interpolación de la spline</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation>Radio del agujero central</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation>Crear pasadores en el lugar</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation>Centrar el eje Z del pasador a los discos generados</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation>Mostrar disco de levas principal</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation>Mostrar otro disco de leva invertido en la parte superior</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation>Simple</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation>Módulo normal si properties_from_tool es verdadero, en caso contrario es el módulo transversal.</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation>Diámetro interior</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation>Diámetro raíz</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation>Paso transversal</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation>Diámetro exterior</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation>Dirección de retroceso</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation>Desplazamiento</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation>Si se indica beta y se activa properties_from_tool, los parámetros del engranaje se vuelven a calcular internamente para el engranaje girado</translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation>Objeto engranaje Python</translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation>Socavado</translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation>Diámetro exterior</translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation>Módulo transversal del engranaje generado</translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation>El radio del perno de la cremallera/cadena</translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation>Tipo de piñon de sincronización</translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation>Paso del engranaje</translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation>Altura radial del diente</translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation>diferencia radial entre el diámetro de paso y la cabeza del engranaje</translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation>Radio del primer arco</translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation>Radio del segundo arco</translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation>Radios del tercer radio</translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation>Desplazamiento en x del segundo punto medio del arco</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation>Altura radial del diente</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation>Distancia radial desde la cabeza del diente a círculo de paso</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation>Ángulo de los flancos de los dientes</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation>Altura de extrusión</translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation>Diámetro</translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation>Revertir rotación de hélice</translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation>Engranaje cónico</translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation>Crear un engranaje cónico</translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation>Engranaje de corona</translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation>Crear un engranaje de corona</translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation>Engranaje cicloide</translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation>Crear un engranaje cicloide</translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation>Cremallera cicloide</translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation>Crear una cremallera cicloide</translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation>Combinar dos engranajes</translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation>Engranaje hipocicloide</translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation>Crear un engranaje hipocicloide con sus pasadores</translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation>Engranaje evolvente interno</translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation>Crear un engranaje evolvente interno</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation>Engranaje evolvente</translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation>Crear un engranaje evolvente externo</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation>Cremallera evolvente</translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation>Crear un cremallera evolvente</translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation>Piñon de linterna</translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation>Crear un piñon de linterna</translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation>Piñon de sincronización</translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation>Crear un piñon de sincronización</translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation>Piñon de sincronización con forma de T</translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation>Crear un piñon de sincronización con forma de T</translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation>Tornillo sin fin</translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation>Crear un tornillo sin fin</translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation>Por favor seleccione dos objetos de tipo engranaje.</translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation>El objeto seleccionado no es un engranaje.</translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation>Módulo Gear: Engranaje de corona creado, preview_mode=true para un rendimiento mejorado. Establezca la propiedad preview_mode en falso cuando esté listo para cortar los dientes.</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation>Generando disco de leva
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation>Generando disco de leva secundario
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation>Generando pasadores
</translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation>Comprobando versión de FreeCAD
</translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation>La versión de FreeCAD (actualmente {}.{}.{} ({})) debe de ser por lo menos {}.{}.{} ({}) para poder trabajar con Python 3.11 y versiones superiores
</translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation>Migrando propiedad de &apos;teeth&apos; a &apos;num_teeth&apos; en la parte {}
</translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation>Engranajes</translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation>Banco de trabajo Engranajes</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@@ -0,0 +1,720 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="pl" sourcelanguage="en_US">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation>wersja środowiska FreeCAD.gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation>liczba zębów</translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation>wysokość</translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation>kąt_skoku</translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation>kąt_natarcia</translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation>moduł</translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation>pasowanie</translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translation>ilość węzłów linii złożonej</translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation type="unfinished">if value is true the gears outer face will match the z=0 plane</translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation type="unfinished">The arc length on the pitch circle by which the tooth thicknes is reduced.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation type="unfinished">test</translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation type="unfinished">angle used for spiral bevel-gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation>Średnica podziałki.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation type="unfinished">The angle by which this gear can turn without moving the mating gear.</translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation>koło zębate główne</translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation>koło zębate podrzędne</translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation>kąt, pod którym ustawiony jest drugie koło zębate</translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation>liczba zębów drugiego koła zębatego</translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation>grubość</translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation>kąt nacisku</translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation>liczba profili używanych dla loftu</translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation>jeśli prawda, żadna operacja logiczna nie będzie wykonana</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation>obiekt Pythona</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation>podwójna helisa</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation>beta</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation type="unfinished">a fillet for the tooth-head, radius = head_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation type="unfinished">a fillet for the tooth-root, radius = root_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation type="unfinished">head_value * module_value = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation type="unfinished">inner_diameter divided by module (hypocycloid)</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation type="unfinished">outer_diameter divided by module (epicycloid)</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation type="unfinished">if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation type="unfinished">pitch in the transverse plane</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation type="unfinished">if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation>głowica * moduł = dodatkowa długość głowicy</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation type="unfinished">clearance * module = additional length of root</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation type="unfinished">Pin ball circle radius (overrides Tooth Pitch)</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation type="unfinished">Roller Diameter</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation type="unfinished">Eccentricity</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation>Ograniczenie kąta nacisku</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation>Przesunięcie w kącie nacisku</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation type="unfinished">Number of teeth in Cam</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation>Liczba punktów użytych do interpolacji splajnu</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation>Promień środkowego otworu</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation>Tworzenie pinezek w miejscu</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation>Centrująca pinezka na osi Z do wygenerowania dysków</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation type="unfinished">Show main cam disk</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation type="unfinished">Show another reversed cam disk on top</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation>prosty</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation>normalny moduł, jeśli properties_from_tool=Prawda, w przeciwnym razie jest to moduł poprzeczny.</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation>średnica wewnętrzna</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation> otworu</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation type="unfinished">transverse_pitch</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation>Średnica zewnętrzna</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation>Kierunek luzu</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation>przesunięcie</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation type="unfinished">if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation type="unfinished">python gear object</translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation type="unfinished">undercut</translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation> zewnętrzna</translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation type="unfinished">traverse module of the generated gear</translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation type="unfinished">the bolt radius of the rack/chain</translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation type="unfinished">type of timing-gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation>skok koła zębatego</translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation>promieniowa wysokość zęba</translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation type="unfinished">radial difference between pitch diameter and head of gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation>promień pierwszego łuku</translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation>promień drugiego łuku</translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation>promień trzeciego łuku</translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation type="unfinished">x-offset of second arc-midpoint</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation>promieniowa wysokość zęba</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation type="unfinished">radial distance from tooth-head to pitch circle</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation type="unfinished">angle of tooth flanks</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation>wysokość wyciągnięcia</translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation>średnica</translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation>odwrotny skręt helisy</translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation type="unfinished">Bevel Gear</translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation type="unfinished">Create a Bevel gear</translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation type="unfinished">Crown Gear</translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation type="unfinished">Create a Crown gear</translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation>Przekładnia cykloidalna</translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation>Utwórz przekładnię cykloidalną</translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation type="unfinished">Cycloid Rack</translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation type="unfinished">Create an Cycloid rack</translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation type="unfinished">Combine two gears</translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation type="unfinished">HypoCycloid Gear</translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation type="unfinished">Create a HypoCycloid gear with its pins</translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation type="unfinished">Internal Involute Gear</translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation type="unfinished">Create an internal involute gear</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation type="unfinished">Involute Gear</translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation type="unfinished">Create an external involute gear</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation type="unfinished">Involute Rack</translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation type="unfinished">Create an Involute rack</translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation type="unfinished">Lantern Gear</translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation type="unfinished">Create a Lantern gear</translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation type="unfinished">Timing Gear</translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation type="unfinished">Create a Timing gear</translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation type="unfinished">Timing Gear T-shape</translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation type="unfinished">Create a Timing gear T-shape</translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation>Przekładnia ślimakowa</translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation>Utwórz przekładnię ślimakową</translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation>Wybierz dwa obiekty przekładni.</translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation>Wybrany obiekt nie jest kołem zębatym.</translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation>Moduł koła zębatego: Utworzono koło zębate koronowe, tryb podglądu = prawda dla lepszej wydajności. Ustaw właściwość preview_mode na fałsz, gdy będziesz gotowy do wycięcia zębów.</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation type="unfinished">Generating cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation type="unfinished">Generating secondary cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation type="unfinished">Generating pins
</translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation>Sprawdzanie wersji FreeCAD
</translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation>Wersja FreeCAD (obecnie {}.{}.{} ({}))
musi to być co najmniej {}.{}.{} ({}),
aby zapewnić zgodność ze środowiskiem Python 3.11 i nowszym.
</translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation type="unfinished">Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation>Zębatka</translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation>Środowisko pracy Zębatka</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@@ -0,0 +1,718 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_TW" sourcelanguage="en_US">
<context>
<name>App::Property</name>
<message>
<location filename="../basegear.py" line="101"/>
<location filename="../connector.py" line="72"/>
<source>freecad.gears-version</source>
<translation>freecad.-</translation>
</message>
<message>
<location filename="../bevelgear.py" line="45"/>
<location filename="../crowngear.py" line="44"/>
<location filename="../cycloidgear.py" line="47"/>
<location filename="../cycloidgearrack.py" line="42"/>
<location filename="../internalinvolutegear.py" line="59"/>
<location filename="../involutegear.py" line="86"/>
<location filename="../involutegearrack.py" line="40"/>
<location filename="../lanterngear.py" line="40"/>
<location filename="../timinggear.py" line="112"/>
<location filename="../timinggear_t.py" line="46"/>
<location filename="../wormgear.py" line="41"/>
<source>number of teeth</source>
<translation></translation>
</message>
<message>
<location filename="../bevelgear.py" line="51"/>
<location filename="../crowngear.py" line="62"/>
<location filename="../cycloidgear.py" line="59"/>
<location filename="../cycloidgearrack.py" line="48"/>
<location filename="../hypocycloidgear.py" line="105"/>
<location filename="../hypocycloidgear.py" line="130"/>
<location filename="../internalinvolutegear.py" line="74"/>
<location filename="../involutegear.py" line="101"/>
<location filename="../involutegearrack.py" line="46"/>
<location filename="../lanterngear.py" line="58"/>
<location filename="../timinggear.py" line="124"/>
<location filename="../wormgear.py" line="53"/>
<source>height</source>
<translation></translation>
</message>
<message>
<location filename="../bevelgear.py" line="57"/>
<source>pitch_angle</source>
<translation type="unfinished">pitch_angle</translation>
</message>
<message>
<location filename="../bevelgear.py" line="63"/>
<source>pressure_angle</source>
<translation type="unfinished">pressure_angle</translation>
</message>
<message>
<location filename="../bevelgear.py" line="69"/>
<location filename="../crowngear.py" line="56"/>
<location filename="../cycloidgear.py" line="53"/>
<location filename="../involutegearrack.py" line="52"/>
<location filename="../lanterngear.py" line="46"/>
<location filename="../wormgear.py" line="47"/>
<source>module</source>
<translation></translation>
</message>
<message>
<location filename="../bevelgear.py" line="75"/>
<location filename="../cycloidgear.py" line="136"/>
<location filename="../internalinvolutegear.py" line="225"/>
<location filename="../involutegear.py" line="231"/>
<source>clearance</source>
<translation type="unfinished">clearance</translation>
</message>
<message>
<location filename="../bevelgear.py" line="81"/>
<location filename="../cycloidgear.py" line="65"/>
<location filename="../cycloidgearrack.py" line="70"/>
<location filename="../internalinvolutegear.py" line="86"/>
<location filename="../involutegear.py" line="253"/>
<source>number of points for spline</source>
<translation type="unfinished">number of points for spline</translation>
</message>
<message>
<location filename="../bevelgear.py" line="90"/>
<source>if value is true the gears outer face will match the z=0 plane</source>
<translation type="unfinished">if value is true the gears outer face will match the z=0 plane</translation>
</message>
<message>
<location filename="../bevelgear.py" line="99"/>
<location filename="../cycloidgear.py" line="145"/>
<location filename="../internalinvolutegear.py" line="204"/>
<location filename="../involutegear.py" line="219"/>
<location filename="../timinggear_t.py" line="69"/>
<source>The arc length on the pitch circle by which the tooth thicknes is reduced.</source>
<translation type="unfinished">The arc length on the pitch circle by which the tooth thicknes is reduced.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="105"/>
<location filename="../cycloidgearrack.py" line="76"/>
<location filename="../internalinvolutegear.py" line="92"/>
<location filename="../involutegearrack.py" line="73"/>
<source>test</source>
<translation></translation>
</message>
<message>
<location filename="../bevelgear.py" line="111"/>
<source>angle used for spiral bevel-gears</source>
<translation type="unfinished">angle used for spiral bevel-gears</translation>
</message>
<message>
<location filename="../bevelgear.py" line="117"/>
<location filename="../cycloidgear.py" line="179"/>
<location filename="../internalinvolutegear.py" line="144"/>
<location filename="../involutegear.py" line="185"/>
<source>The pitch diameter.</source>
<translation type="unfinished">The pitch diameter.</translation>
</message>
<message>
<location filename="../bevelgear.py" line="132"/>
<location filename="../cycloidgear.py" line="194"/>
<location filename="../internalinvolutegear.py" line="153"/>
<location filename="../involutegear.py" line="195"/>
<source>The angle by which this gear can turn without moving the mating gear.</source>
<translation type="unfinished">The angle by which this gear can turn without moving the mating gear.</translation>
</message>
<message>
<location filename="../connector.py" line="79"/>
<source>master gear</source>
<translation type="unfinished">master gear</translation>
</message>
<message>
<location filename="../connector.py" line="86"/>
<source>slave gear</source>
<translation type="unfinished">slave gear</translation>
</message>
<message>
<location filename="../connector.py" line="93"/>
<location filename="../connector.py" line="100"/>
<source>angle at which second gear is placed</source>
<translation type="unfinished">angle at which second gear is placed</translation>
</message>
<message>
<location filename="../crowngear.py" line="50"/>
<source>number of teeth of other gear</source>
<translation type="unfinished">number of teeth of other gear</translation>
</message>
<message>
<location filename="../crowngear.py" line="68"/>
<location filename="../cycloidgearrack.py" line="54"/>
<location filename="../internalinvolutegear.py" line="80"/>
<location filename="../involutegearrack.py" line="58"/>
<source>thickness</source>
<translation></translation>
</message>
<message>
<location filename="../crowngear.py" line="74"/>
<location filename="../internalinvolutegear.py" line="239"/>
<location filename="../involutegear.py" line="107"/>
<location filename="../involutegearrack.py" line="160"/>
<location filename="../wormgear.py" line="72"/>
<source>pressure angle</source>
<translation type="unfinished">pressure angle</translation>
</message>
<message>
<location filename="../crowngear.py" line="101"/>
<location filename="../lanterngear.py" line="64"/>
<source>number of profiles used for loft</source>
<translation type="unfinished">number of profiles used for loft</translation>
</message>
<message>
<location filename="../crowngear.py" line="107"/>
<source>if true no boolean operation is done</source>
<translation type="unfinished">if true no boolean operation is done</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="71"/>
<source>the python object</source>
<translation type="unfinished">the python object</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="102"/>
<location filename="../cycloidgearrack.py" line="110"/>
<location filename="../internalinvolutegear.py" line="253"/>
<location filename="../involutegear.py" line="162"/>
<location filename="../involutegearrack.py" line="116"/>
<source>double helix</source>
<translation type="unfinished">double helix</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="108"/>
<location filename="../cycloidgearrack.py" line="104"/>
<location filename="../internalinvolutegear.py" line="247"/>
<location filename="../involutegear.py" line="156"/>
<location filename="../involutegearrack.py" line="110"/>
<location filename="../wormgear.py" line="65"/>
<source>beta</source>
<translation type="unfinished">beta</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="119"/>
<location filename="../cycloidgearrack.py" line="175"/>
<location filename="../internalinvolutegear.py" line="184"/>
<location filename="../involutegear.py" line="130"/>
<location filename="../involutegearrack.py" line="171"/>
<location filename="../timinggear_t.py" line="78"/>
<source>a fillet for the tooth-head, radius = head_fillet x module</source>
<translation type="unfinished">a fillet for the tooth-head, radius = head_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="128"/>
<location filename="../cycloidgearrack.py" line="184"/>
<location filename="../internalinvolutegear.py" line="193"/>
<location filename="../involutegear.py" line="139"/>
<location filename="../involutegearrack.py" line="180"/>
<location filename="../timinggear_t.py" line="87"/>
<source>a fillet for the tooth-root, radius = root_fillet x module</source>
<translation type="unfinished">a fillet for the tooth-root, radius = root_fillet x module</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="153"/>
<location filename="../internalinvolutegear.py" line="219"/>
<location filename="../involutegear.py" line="239"/>
<source>head_value * module_value = additional length of head</source>
<translation type="unfinished">head_value * module_value = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="163"/>
<location filename="../cycloidgearrack.py" line="156"/>
<source>inner_diameter divided by module (hypocycloid)</source>
<translation type="unfinished">inner_diameter divided by module (hypocycloid)</translation>
</message>
<message>
<location filename="../cycloidgear.py" line="171"/>
<location filename="../cycloidgearrack.py" line="164"/>
<source>outer_diameter divided by module (epicycloid)</source>
<translation type="unfinished">outer_diameter divided by module (epicycloid)</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="64"/>
<location filename="../involutegearrack.py" line="67"/>
<source>if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</source>
<translation type="unfinished">if enabled the rack is drawn with a constant number of teeth to avoid topologic renaming.</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="118"/>
<location filename="../involutegearrack.py" line="124"/>
<source>pitch in the transverse plane</source>
<translation type="unfinished">pitch in the transverse plane</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="128"/>
<location filename="../involutegearrack.py" line="134"/>
<source>if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</source>
<translation type="unfinished">if enabled the total length of the rack is teeth x pitch, otherwise the rack starts with a tooth-flank</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="138"/>
<location filename="../involutegearrack.py" line="144"/>
<location filename="../lanterngear.py" line="72"/>
<location filename="../wormgear.py" line="86"/>
<source>head * module = additional length of head</source>
<translation type="unfinished">head * module = additional length of head</translation>
</message>
<message>
<location filename="../cycloidgearrack.py" line="146"/>
<location filename="../involutegearrack.py" line="152"/>
<location filename="../wormgear.py" line="94"/>
<source>clearance * module = additional length of root</source>
<translation type="unfinished">clearance * module = additional length of root</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="48"/>
<source>Pin ball circle radius (overrides Tooth Pitch)</source>
<translation type="unfinished">Pin ball circle radius (overrides Tooth Pitch)</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="54"/>
<source>Roller Diameter</source>
<translation type="unfinished">Roller Diameter</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="60"/>
<source>Eccentricity</source>
<translation type="unfinished">Eccentricity</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="66"/>
<source>Pressure angle limit</source>
<translation type="unfinished">Pressure angle limit</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="72"/>
<source>Offset in pressure angle</source>
<translation type="unfinished">Offset in pressure angle</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="78"/>
<source>Number of teeth in Cam</source>
<translation type="unfinished">Number of teeth in Cam</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="86"/>
<source>Number of points used for spline interpolation</source>
<translation type="unfinished">Number of points used for spline interpolation</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="92"/>
<source>Center hole&apos;s radius</source>
<translation type="unfinished">Center hole&apos;s radius</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="99"/>
<source>Create pins in place</source>
<translation type="unfinished">Create pins in place</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="111"/>
<source>Center pin Z axis to generated disks</source>
<translation type="unfinished">Center pin Z axis to generated disks</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="118"/>
<source>Show main cam disk</source>
<translation type="unfinished">Show main cam disk</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="124"/>
<source>Show another reversed cam disk on top</source>
<translation type="unfinished">Show another reversed cam disk on top</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="53"/>
<location filename="../involutegear.py" line="247"/>
<source>simple</source>
<translation></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="68"/>
<location filename="../involutegear.py" line="95"/>
<source>normal module if properties_from_tool=True, else it&apos;s the transverse module.</source>
<translation type="unfinished">normal module if properties_from_tool=True, else it&apos;s the transverse module.</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="128"/>
<source>inside diameter</source>
<translation></translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="135"/>
<location filename="../involutegear.py" line="177"/>
<source>root diameter</source>
<translation type="unfinished">root diameter</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="165"/>
<location filename="../involutegear.py" line="207"/>
<source>transverse_pitch</source>
<translation type="unfinished">transverse_pitch</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="172"/>
<source>Outside diameter</source>
<translation type="unfinished">Outside diameter</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="210"/>
<location filename="../involutegear.py" line="225"/>
<source>backlash direction</source>
<translation type="unfinished">backlash direction</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="233"/>
<location filename="../involutegear.py" line="113"/>
<source>shift</source>
<translation type="unfinished">shift</translation>
</message>
<message>
<location filename="../internalinvolutegear.py" line="262"/>
<location filename="../involutegear.py" line="150"/>
<location filename="../involutegearrack.py" line="104"/>
<source>if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</source>
<translation type="unfinished">if beta is given and properties_from_tool is enabled, gear parameters are internally recomputed for the rotated gear</translation>
</message>
<message>
<location filename="../involutegear.py" line="49"/>
<source>python gear object</source>
<translation type="unfinished">python gear object</translation>
</message>
<message>
<location filename="../involutegear.py" line="121"/>
<source>undercut</source>
<translation type="unfinished">undercut</translation>
</message>
<message>
<location filename="../involutegear.py" line="170"/>
<source>outside diameter</source>
<translation type="unfinished">outside diameter</translation>
</message>
<message>
<location filename="../involutegear.py" line="261"/>
<source>traverse module of the generated gear</source>
<translation type="unfinished">traverse module of the generated gear</translation>
</message>
<message>
<location filename="../lanterngear.py" line="52"/>
<source>the bolt radius of the rack/chain</source>
<translation type="unfinished">the bolt radius of the rack/chain</translation>
</message>
<message>
<location filename="../timinggear.py" line="118"/>
<source>type of timing-gear</source>
<translation type="unfinished">type of timing-gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="130"/>
<location filename="../timinggear_t.py" line="40"/>
<source>pitch of gear</source>
<translation type="unfinished">pitch of gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="137"/>
<source>radial height of teeth</source>
<translation type="unfinished">radial height of teeth</translation>
</message>
<message>
<location filename="../timinggear.py" line="147"/>
<source>radial difference between pitch diameter and head of gear</source>
<translation type="unfinished">radial difference between pitch diameter and head of gear</translation>
</message>
<message>
<location filename="../timinggear.py" line="154"/>
<source>radius of first arc</source>
<translation type="unfinished">radius of first arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="161"/>
<source>radius of second arc</source>
<translation type="unfinished">radius of second arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="168"/>
<source>radius of third arc</source>
<translation type="unfinished">radius of third arc</translation>
</message>
<message>
<location filename="../timinggear.py" line="175"/>
<source>x-offset of second arc-midpoint</source>
<translation type="unfinished">x-offset of second arc-midpoint</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="52"/>
<source>radial height of tooth</source>
<translation type="unfinished">radial height of tooth</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="60"/>
<source>radial distance from tooth-head to pitch circle</source>
<translation type="unfinished">radial distance from tooth-head to pitch circle</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="93"/>
<source>angle of tooth flanks</source>
<translation type="unfinished">angle of tooth flanks</translation>
</message>
<message>
<location filename="../timinggear_t.py" line="99"/>
<source>extrusion height</source>
<translation type="unfinished">extrusion height</translation>
</message>
<message>
<location filename="../wormgear.py" line="59"/>
<source>diameter</source>
<translation type="unfinished">diameter</translation>
</message>
<message>
<location filename="../wormgear.py" line="78"/>
<source>reverse rotation of helix</source>
<translation type="unfinished">reverse rotation of helix</translation>
</message>
</context>
<context>
<name>FCGear_BevelGear</name>
<message>
<location filename="../commands.py" line="159"/>
<source>Bevel Gear</source>
<translation type="unfinished">Bevel Gear</translation>
</message>
<message>
<location filename="../commands.py" line="162"/>
<source>Create a Bevel gear</source>
<translation type="unfinished">Create a Bevel gear</translation>
</message>
</context>
<context>
<name>FCGear_CrownGear</name>
<message>
<location filename="../commands.py" line="143"/>
<source>Crown Gear</source>
<translation type="unfinished">Crown Gear</translation>
</message>
<message>
<location filename="../commands.py" line="146"/>
<source>Create a Crown gear</source>
<translation type="unfinished">Create a Crown gear</translation>
</message>
</context>
<context>
<name>FCGear_CycloidGear</name>
<message>
<location filename="../commands.py" line="151"/>
<source>Cycloid Gear</source>
<translation type="unfinished">Cycloid Gear</translation>
</message>
<message>
<location filename="../commands.py" line="154"/>
<source>Create a Cycloid gear</source>
<translation type="unfinished">Create a Cycloid gear</translation>
</message>
</context>
<context>
<name>FCGear_CycloidRack</name>
<message>
<location filename="../commands.py" line="135"/>
<source>Cycloid Rack</source>
<translation type="unfinished">Cycloid Rack</translation>
</message>
<message>
<location filename="../commands.py" line="138"/>
<source>Create an Cycloid rack</source>
<translation type="unfinished">Create an Cycloid rack</translation>
</message>
</context>
<context>
<name>FCGear_GearConnector</name>
<message>
<location filename="../commands.py" line="209"/>
<location filename="../commands.py" line="211"/>
<source>Combine two gears</source>
<translation type="unfinished">Combine two gears</translation>
</message>
</context>
<context>
<name>FCGear_HypoCycloidGear</name>
<message>
<location filename="../commands.py" line="167"/>
<source>HypoCycloid Gear</source>
<translation type="unfinished">HypoCycloid Gear</translation>
</message>
<message>
<location filename="../commands.py" line="172"/>
<source>Create a HypoCycloid gear with its pins</source>
<translation type="unfinished">Create a HypoCycloid gear with its pins</translation>
</message>
</context>
<context>
<name>FCGear_InternalInvoluteGear</name>
<message>
<location filename="../commands.py" line="117"/>
<source>Internal Involute Gear</source>
<translation type="unfinished">Internal Involute Gear</translation>
</message>
<message>
<location filename="../commands.py" line="122"/>
<source>Create an internal involute gear</source>
<translation type="unfinished">Create an internal involute gear</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteGear</name>
<message>
<location filename="../commands.py" line="105"/>
<source>Involute Gear</source>
<translation type="unfinished">Involute Gear</translation>
</message>
<message>
<location filename="../commands.py" line="110"/>
<source>Create an external involute gear</source>
<translation type="unfinished">Create an external involute gear</translation>
</message>
</context>
<context>
<name>FCGear_InvoluteRack</name>
<message>
<location filename="../commands.py" line="127"/>
<source>Involute Rack</source>
<translation type="unfinished">Involute Rack</translation>
</message>
<message>
<location filename="../commands.py" line="130"/>
<source>Create an Involute rack</source>
<translation type="unfinished">Create an Involute rack</translation>
</message>
</context>
<context>
<name>FCGear_LanternGear</name>
<message>
<location filename="../commands.py" line="201"/>
<source>Lantern Gear</source>
<translation type="unfinished">Lantern Gear</translation>
</message>
<message>
<location filename="../commands.py" line="204"/>
<source>Create a Lantern gear</source>
<translation type="unfinished">Create a Lantern gear</translation>
</message>
</context>
<context>
<name>FCGear_TimingGear</name>
<message>
<location filename="../commands.py" line="193"/>
<source>Timing Gear</source>
<translation type="unfinished">Timing Gear</translation>
</message>
<message>
<location filename="../commands.py" line="196"/>
<source>Create a Timing gear</source>
<translation type="unfinished">Create a Timing gear</translation>
</message>
</context>
<context>
<name>FCGear_TimingGearT</name>
<message>
<location filename="../commands.py" line="185"/>
<source>Timing Gear T-shape</source>
<translation type="unfinished">Timing Gear T-shape</translation>
</message>
<message>
<location filename="../commands.py" line="188"/>
<source>Create a Timing gear T-shape</source>
<translation type="unfinished">Create a Timing gear T-shape</translation>
</message>
</context>
<context>
<name>FCGear_WormGear</name>
<message>
<location filename="../commands.py" line="177"/>
<source>Worm Gear</source>
<translation type="unfinished">Worm Gear</translation>
</message>
<message>
<location filename="../commands.py" line="180"/>
<source>Create a Worm gear</source>
<translation type="unfinished">Create a Worm gear</translation>
</message>
</context>
<context>
<name>Log</name>
<message>
<location filename="../commands.py" line="218"/>
<source>Please select two gear objects.</source>
<translation type="unfinished">Please select two gear objects.</translation>
</message>
<message>
<location filename="../commands.py" line="224"/>
<source>Selected object is not a gear.</source>
<translation type="unfinished">Selected object is not a gear.</translation>
</message>
<message>
<location filename="../crowngear.py" line="94"/>
<source>Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</source>
<translation type="unfinished">Gear module: Crown gear created, preview_mode = true for improved performance. Set preview_mode property to false when ready to cut teeth.</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="227"/>
<source>Generating cam disk
</source>
<translation type="unfinished">Generating cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="269"/>
<source>Generating secondary cam disk
</source>
<translation type="unfinished">Generating secondary cam disk
</translation>
</message>
<message>
<location filename="../hypocycloidgear.py" line="287"/>
<source>Generating pins
</source>
<translation type="unfinished">Generating pins
</translation>
</message>
<message>
<location filename="../init_gui.py" line="39"/>
<source>Checking FreeCAD version
</source>
<translation type="unfinished">Checking FreeCAD version
</translation>
</message>
<message>
<location filename="../init_gui.py" line="73"/>
<source>FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</source>
<translation type="unfinished">FreeCAD version (currently {}.{}.{} ({})) must be at least {}.{}.{} ({}) in order to work with Python 3.11 and above
</translation>
</message>
<message>
<location filename="../basegear.py" line="137"/>
<source>Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</source>
<translation type="unfinished">Migrating &apos;teeth&apos; property to &apos;num_teeth&apos; on {} part
</translation>
</message>
</context>
<context>
<name>Workbench</name>
<message>
<location filename="../init_gui.py" line="90"/>
<location filename="../init_gui.py" line="129"/>
<location filename="../init_gui.py" line="130"/>
<source>Gear</source>
<translation type="unfinished">Gear</translation>
</message>
<message>
<location filename="../init_gui.py" line="91"/>
<source>Gear Workbench</source>
<translation type="unfinished">Gear Workbench</translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,116 @@
# About translating Gear Workbench
<!--toc:start-->
- [About translating Gear Workbench](#about-translating-gear-workbench)
- [Updating translations template file](#updating-translations-template-file)
- [Creating file for missing locale](#creating-file-for-missing-locale)
- [Using script](#using-script)
- [Renaming file](#renaming-file)
- [Translating](#translating)
- [Compiling translations](#compiling-translations)
- [Sending translations](#sending-translations)
- [More information](#more-information)
<!--toc:end-->
> [!NOTE]
> All commands **must** be run in `./freecad/gears/translations/` directory.
> [!IMPORTANT]
> If you want to update/release the files you need to have installed
> `lupdate` and `lrelease` from Qt6 version. Using the versions from
> Qt5 is not advised because they're buggy.
## Updating translations template file
To update the template file from source files you should use this command:
```shell
./update_translation.sh -U
```
Once done you can commit the changes and upload the new file to CrowdIn platform
at <https://crowdin.com/project/freecad-addons> webpage and find the **Gear** project.
## Creating file for missing locale
### Using script
To create a file for a new language with all **Gear** translatable strings execute
the script with `-u` flag plus your locale:
```shell
./update_translation.sh -u de
```
### Renaming file
Also you can rename new `Gear.ts` file by appending the locale code,
for example, `Gear_de.ts` for German and change
```xml
<TS version="2.1">
```
to
```xml
<TS version="2.1" language="de" sourcelanguage="en">
```
As of 13/09/2024 the supported locales on FreeCAD
(according to `FreeCADGui.supportedLocales()`) are 43:
```python
{'English': 'en', 'Afrikaans': 'af', 'Arabic': 'ar', 'Basque': 'eu',
'Belarusian': 'be', 'Bulgarian': 'bg', 'Catalan': 'ca',
'Chinese Simplified': 'zh-CN', 'Chinese Traditional': 'zh-TW', 'Croatian': 'hr',
'Czech': 'cs', 'Dutch': 'nl', 'Filipino': 'fil', 'Finnish': 'fi', 'French': 'fr',
'Galician': 'gl', 'Georgian': 'ka', 'German': 'de', 'Greek': 'el', 'Hungarian': 'hu',
'Indonesian': 'id', 'Italian': 'it', 'Japanese': 'ja', 'Kabyle': 'kab',
'Korean': 'ko', 'Lithuanian': 'lt', 'Norwegian': 'no', 'Polish': 'pl',
'Portuguese': 'pt-PT', 'Portuguese, Brazilian': 'pt-BR', 'Romanian': 'ro',
'Russian': 'ru', 'Serbian': 'sr', 'Serbian, Latin': 'sr-CS', 'Slovak': 'sk',
'Slovenian': 'sl', 'Spanish': 'es-ES', 'Spanish, Argentina': 'es-AR',
'Swedish': 'sv-SE', 'Turkish': 'tr', 'Ukrainian': 'uk', 'Valencian': 'val-ES',
'Vietnamese': 'vi'}
```
## Translating
To edit your language file open your file in `Qt Linguist` from `qt5-tools`/`qt6-tools`
package or in a text editor like `xed`, `mousepad`, `gedit`, `nano`, `vim`/`nvim`,
`geany` etc. and translate it.
Alternatively you can visit the **FreeCAD-addons** project on CrowdIn platform
at <https://crowdin.com/project/freecad-addons> webpage and find your language,
once done, look for the **Gear** project.
## Compiling translations
To convert all `.ts` files to `.qm` files (merge) you can use this command:
```shell
./update_translation.sh -R
```
If you are a translator that wants to update only their language file
to test it on **FreeCAD** before doing a PR you can use this command:
```shell
./update_translation.sh -r de
```
This will update the `.qm` file for your language (German in this case).
## Sending translations
Now you can contribute your translated `.ts` file to **Gear** repository,
also include the `.qm` file.
<https://github.com/looooo/freecad.gears>
## More information
You can read more about translating external workbenches here:
<https://wiki.freecad.org/Translating_an_external_workbench>

View File

@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# --------------------------------------------------------------------------------------------------
#
# Create, update and release translation files.
#
# Supported locales on FreeCAD <2024-11-30, FreeCADGui.supportedLocales(), total=44>:
# {'English': 'en', 'Afrikaans': 'af', 'Arabic': 'ar', 'Basque': 'eu', 'Belarusian': 'be',
# 'Bulgarian': 'bg', 'Catalan': 'ca', 'Chinese Simplified': 'zh-CN',
# 'Chinese Traditional': 'zh-TW', 'Croatian': 'hr', 'Czech': 'cs', 'Danish': 'da',
# 'Dutch': 'nl', 'Filipino': 'fil', 'Finnish': 'fi', 'French': 'fr', 'Galician': 'gl',
# 'Georgian': 'ka', 'German': 'de', 'Greek': 'el', 'Hungarian': 'hu', 'Indonesian': 'id',
# 'Italian': 'it', 'Japanese': 'ja', 'Kabyle': 'kab', 'Korean': 'ko', 'Lithuanian': 'lt',
# 'Norwegian': 'no', 'Polish': 'pl', 'Portuguese': 'pt-PT', 'Portuguese, Brazilian': 'pt-BR',
# 'Romanian': 'ro', 'Russian': 'ru', 'Serbian': 'sr', 'Serbian, Latin': 'sr-CS', 'Slovak': 'sk',
# 'Slovenian': 'sl', 'Spanish': 'es-ES', 'Spanish, Argentina': 'es-AR', 'Swedish': 'sv-SE',
# 'Turkish': 'tr', 'Ukrainian': 'uk', 'Valencian': 'val-ES', 'Vietnamese': 'vi'}
#
# NOTE: PREPARATION
# - Install Qt tools
# Debian-based (e.g., Ubuntu): $ sudo apt-get install qttools5-dev-tools pyqt6-dev-tools
# Fedora-based: $ sudo dnf install qt6-linguist qt6-devel
# Arch-based: $ sudo pacman -S qt6-tools python-pyqt6
# - Make the script executable
# $ chmod +x update_translation.sh
# - The script has to be executed within the `freecad/gears/translations` directory.
# Executing the script with no flags invokes the help.
# $ ./update_translation.sh
#
# NOTE: WORKFLOW TRANSLATOR (LOCAL)
# - Execute the script passing the `-u` flag plus locale code as argument
# Only update the file(s) you're translating!
# $ ./update_translation.sh -u es-ES
# - Do the translation via Qt Linguist and use `File>Release`
# - If releasing with the script execute it passing the `-r` flag
# plus locale code as argument
# $ ./update_translation.sh -r es-ES
#
# NOTE: WORKFLOW MAINTAINER (CROWDIN)
# - Execute the script passing the '-U' flag
# $ ./update_translation.sh -U
# - Upload the updated file to Crowdin and wait for translators do their thing ;-)
# - Once done, download the translated files, copy them to `freecad/gears/translations`
# and release all the files to update the changes
# $ ./update_translation.sh -R
#
# --------------------------------------------------------------------------------------------------
supported_locales=(
"en" "af" "ar" "eu" "be" "bg" "ca" "zh-CN" "zh-TW" "hr"
"cs" "da" "nl" "fil" "fi" "fr" "gl" "ka" "de" "el"
"hu" "id" "it" "ja" "kab" "ko" "lt" "no" "pl" "pt-PT"
"pt-BR" "ro" "ru" "sr" "sr-CS" "sk" "sl" "es-ES" "es-AR" "sv-SE"
"tr" "uk" "val-ES" "vi"
)
is_locale_supported() {
local locale="$1"
for supported_locale in "${supported_locales[@]}"; do
[ "$supported_locale" == "$locale" ] && return 0
done
return 1
}
update_locale() {
local locale="$1"
local u=${locale:+_} # Conditional underscore
# NOTE: Execute the right command depending on:
# - if it's a locale file or the main, agnostic one
[ ! -f "${WB}${u}${locale}.ts" ] && action="Creating" || action="Updating"
echo -e "\033[1;34m\n\t<<< ${action} '${WB}${u}${locale}.ts' file >>>\n\033[m"
if [ "$u" == "" ]; then
$LUPDATE ../*.py -ts "${WB}.ts" # locale-agnostic file
else
$LUPDATE ../*.py -source-language en_US -target-language "${locale//-/_}" \
-ts "${WB}_${locale}.ts"
fi
}
normalize_crowdin_files() {
# Rename files which locales are different on FreeCAD and delete not supported locales
crowdin_fixes=(af-ZA ar-SA be-BY bg-BG ca-ES cs-CZ da-DK de-DE el-GR eu-ES fi-FI
fil-PH fr-FR gl-ES hr-HR hu-HU it-IT ja-JP ka-GE kab-KAB ko-KR lt-LT nl-NL
no-NO pl-PL ro-RO ru-RU sk-SK sl-SI sr-SP tr-TR uk-UA vi-VN)
crowdin_deletes=(az-AZ bn-BD br-FR bs-BA en en-GB en-US eo-UY es-CO es-VE et-EE fa-IR he-IL
hi-IN hy-AM id-ID kaa lv-LV mk-MK ms-MY sat-IN si-LK ta-IN te-IN th-TH ur-PK xav yo-NG)
for pattern in "${crowdin_fixes[@]}"; do
find . -type f -name "*_${pattern}\.*" | while read -r file; do
new_name=${file//-*./.}
mv -v "$file" "$new_name"
done
done
for pattern in "${crowdin_deletes[@]}"; do
find . -type f -name "*_${pattern}\.*" -delete
done
}
help() {
echo -e "\nDescription:"
echo -e "\tCreate, update and release translation files."
echo -e "\nUsage:"
echo -e "\t./update_translation.sh [-R] [-U] [-r <locale>] [-u <locale>]"
echo -e "\nFlags:"
echo -e " -R\n\tRelease all translations (qm files)"
echo -e " -U\n\tUpdate all translations (ts files)"
echo -e " -r <locale>\n\tRelease the specified locale"
echo -e " -u <locale>\n\tUpdate strings for the specified locale"
echo -e " -N\n\tNormalize CrowdIn filenames"
}
# Main function ------------------------------------------------------------------------------------
LUPDATE=/usr/lib/qt6/bin/lupdate # from Qt6
# LUPDATE=lupdate # from Qt5
LRELEASE=/usr/lib/qt6/bin/lrelease # from Qt6
# LRELEASE=lrelease # from Qt5
WB="Gear"
sed -i '3s/-/_/' ${WB}*.ts # Enforce underscore on locales
sed -i '3s/\"en\"/\"en_US\"/g' ${WB}*.ts # Use en_US
if [ $# -eq 1 ]; then
if [ "$1" == "-R" ]; then
find . -type f -name '*_*.ts' | while IFS= read -r file; do
# Release all locales
$LRELEASE -nounfinished "$file"
echo
done
elif [ "$1" == "-U" ]; then
for locale in "${supported_locales[@]}"; do
update_locale "$locale"
done
elif [ "$1" == "-u" ]; then
update_locale # Update main file (agnostic)
elif [ "$1" == "-N" ]; then
normalize_crowdin_files
else
help
fi
elif [ $# -eq 2 ]; then
LOCALE="$2"
if is_locale_supported "$LOCALE"; then
if [ "$1" == "-r" ]; then
# Release locale (creation of *.qm file from *.ts file)
$LRELEASE -nounfinished "${WB}_${LOCALE}.ts"
elif [ "$1" == "-u" ]; then
# Update main & locale files
update_locale
update_locale "$LOCALE"
fi
else
echo "Verify your language code. Case sensitive."
echo "If it's correct, ask a maintainer to add support for your language on FreeCAD."
echo -e "\nSupported locales, '\033[1;34mFreeCADGui.supportedLocales()\033[m': \033[1;33m"
for locale in $(printf "%s\n" "${supported_locales[@]}" | sort); do
echo -n "$locale "
done
echo
fi
else
help
fi

189
freecad/gears/wormgear.py Normal file
View File

@@ -0,0 +1,189 @@
# -*- 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 <http://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import numpy as np
from freecad import app
from freecad import part
from pygears.involute_tooth import InvoluteTooth
from pygears._functions import rotation
from .basegear import BaseGear, helical_extrusion, fcvec
QT_TRANSLATE_NOOP = app.Qt.QT_TRANSLATE_NOOP
class WormGear(BaseGear):
"""FreeCAD gear rack"""
def __init__(self, obj):
super(WormGear, self).__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"num_teeth",
"base",
QT_TRANSLATE_NOOP("App::Property", "number of teeth"),
)
obj.addProperty(
"App::PropertyLength",
"module",
"base",
QT_TRANSLATE_NOOP("App::Property", "module"),
)
obj.addProperty(
"App::PropertyLength",
"height",
"base",
QT_TRANSLATE_NOOP("App::Property", "height"),
)
obj.addProperty(
"App::PropertyLength",
"diameter",
"base",
QT_TRANSLATE_NOOP("App::Property", "diameter"),
)
obj.addProperty(
"App::PropertyAngle",
"beta",
"computed",
QT_TRANSLATE_NOOP("App::Property", "beta"),
1,
)
obj.addProperty(
"App::PropertyAngle",
"pressure_angle",
"involute",
QT_TRANSLATE_NOOP("App::Property", "pressure angle"),
)
obj.addProperty(
"App::PropertyBool",
"reverse_pitch",
"base",
QT_TRANSLATE_NOOP("App::Property", "reverse rotation of helix"),
)
obj.addProperty(
"App::PropertyFloat",
"head",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "head * module = additional length of head"
),
)
obj.addProperty(
"App::PropertyFloat",
"clearance",
"tolerance",
QT_TRANSLATE_NOOP(
"App::Property", "clearance * module = additional length of root"
),
)
obj.num_teeth = (3, 1, 10, 1) # default, min, max, step
obj.module = "1. mm"
obj.pressure_angle = "20. deg"
obj.height = "5. mm"
obj.diameter = "5. mm"
obj.clearance = 0.25
obj.head = 0
obj.reverse_pitch = False
self.obj = obj
obj.Proxy = self
def generate_gear_shape(self, fp):
m = fp.module.Value
d = fp.diameter.Value
t = fp.num_teeth
h = fp.height
clearance = fp.clearance
head = fp.head
alpha = fp.pressure_angle.Value
beta = np.arctan(m * t / d)
fp.beta = np.rad2deg(beta)
beta = -(fp.reverse_pitch * 2 - 1) * (np.pi / 2 - beta)
r_1 = (d - (2 + 2 * clearance) * m) / 2
r_2 = (d + (2 + 2 * head) * m) / 2
z_a = (2 + head + clearance) * m * np.tan(np.deg2rad(alpha))
z_b = (m * np.pi - 4 * m * np.tan(np.deg2rad(alpha))) / 2
z_0 = clearance * m * np.tan(np.deg2rad(alpha))
z_1 = z_b - z_0
z_2 = z_1 + z_a
z_3 = z_2 + z_b - 2 * head * m * np.tan(np.deg2rad(alpha))
z_4 = z_3 + z_a
def helical_projection(r, z):
phi = 2 * z / m / t
x = r * np.cos(phi)
y = r * np.sin(phi)
z = 0 * y
return np.array([x, y, z]).T
# create a circle from phi=0 to phi_1 with r_1
phi_0 = 2 * z_0 / m / t
phi_1 = 2 * z_1 / m / t
c1 = part.makeCircle(
r_1,
app.Vector(0, 0, 0),
app.Vector(0, 0, 1),
np.rad2deg(phi_0),
np.rad2deg(phi_1),
)
# create first bspline
z_values = np.linspace(z_1, z_2, 10)
r_values = np.linspace(r_1, r_2, 10)
points = helical_projection(r_values, z_values)
bsp1 = part.BSplineCurve()
bsp1.interpolate(list(map(fcvec, points)))
bsp1 = bsp1.toShape()
# create circle from phi_2 to phi_3
phi_2 = 2 * z_2 / m / t
phi_3 = 2 * z_3 / m / t
c2 = part.makeCircle(
r_2,
app.Vector(0, 0, 0),
app.Vector(0, 0, 1),
np.rad2deg(phi_2),
np.rad2deg(phi_3),
)
# create second bspline
z_values = np.linspace(z_3, z_4, 10)
r_values = np.linspace(r_2, r_1, 10)
points = helical_projection(r_values, z_values)
bsp2 = part.BSplineCurve()
bsp2.interpolate(list(map(fcvec, points)))
bsp2 = bsp2.toShape()
wire = part.Wire([c1, bsp1, c2, bsp2])
w_all = [wire]
rot = app.Matrix()
rot.rotateZ(2 * np.pi / t)
for i in range(1, t):
w_all.append(w_all[-1].transformGeometry(rot))
full_wire = part.Wire(w_all)
if h == 0:
return full_wire
else:
shape = helical_extrusion(part.Face(full_wire), h, h * np.tan(beta) * 2 / d)
return shape

3
freecad/part.py Normal file
View File

@@ -0,0 +1,3 @@
# this should be part of FreeCAD itself, remove this file once
# relevant PR is merged
from Part import *

4
metadata.txt Normal file
View File

@@ -0,0 +1,4 @@
[dependencies]
workbenches=
pylibs=scipy
optionalpylibs=

View File

@@ -1,24 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<?xml version="1.0" encoding="UTF-8"?>
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
<name>freecad.gears workbench</name>
<description>A gear workbench for FreeCAD</description>
<version>1.2</version>
<date>2022-02-07</date>
<maintainer email="sppedflyer@gmail.com">looooo</maintainer>
<license file="LICENSE">GPL 3</license>
<url type="repository" branch="master">https://github.com/looooo/freecad.gears</url>
<url type="bugtracker">https://github.com/looooo/freecad.gears/issues</url>
<url type="documentation">https://wiki.freecad.org/FCGear_Workbench</url>
<name>gears</name>
<description>Parametric gear workbench for Kindred Create</description>
<version>1.3.0</version>
<maintainer email="development@kindred-systems.com">Kindred Systems</maintainer>
<license file="LICENSE">GPL-3.0-or-later</license>
<url type="repository">https://git.kindred-systems.com/kindred/gears</url>
<icon>freecad/gears/icons/gearworkbench.svg</icon>
<content>
<workbench>
<classname>GearWorkbench</classname>
<subdirectory>./</subdirectory>
<freecadmin>0.19</freecadmin>
<tag>gear</tag>
<tag>gears</tag>
<subdirectory>freecad/gears</subdirectory>
</workbench>
</content>
<kindred>
<min_create_version>0.1.0</min_create_version>
<load_priority>40</load_priority>
<dependencies>
<dependency>sdk</dependency>
</dependencies>
</kindred>
</package>

18364
pixi.lock Normal file

File diff suppressed because it is too large Load Diff

31
pixi.toml Normal file
View File

@@ -0,0 +1,31 @@
[workspace]
authors = ["looooo <sppedflyer@gmail.com>"]
channels = ["conda-forge"]
description = "Add a short description here"
name = "freecad.gears"
platforms = ["osx-arm64", "linux-aarch64", "linux-64", "win-64", "osx-64"]
version = "0.1.0"
[pypi-dependencies]
freecad-visual-tests = "*"
freecad_gears = { path = ".", editable = true }
pytest = "*"
[tasks]
lint = "pylint $(git ls-files '*.py')"
test = "pytest tests/ -v -m 'not visual'"
test-visual = "pytest tests/ -v -m visual -s"
test-visual-xvfb = "xvfb-run -a pytest tests/ -v -m visual -s"
test-all = "pytest tests/ -v -s"
create-references = "VISUAL_TEST_REFERENCE_MODE=update pytest tests/ -v -m visual -s"
create-references-xvfb = "xvfb-run -a pixi run create-references"
clean-test = "rm -rf tests/data/*/artifacts tests/data/*/references"
[dependencies]
freecad = ">=1.0.0,<2"
numpy = ">=1.26,<1.27"
scipy = "*"
sympy = "*"
jupyter = "*"
pylint = "*"
matplotlib = "*"

View File

@@ -16,4 +16,4 @@
# * *
# ***************************************************************************
__version__ = "1.2.0"
__version__ = "1.3.0"

View File

@@ -16,23 +16,39 @@
# * *
# ***************************************************************************
from __future__ import division
from numpy import sin, cos, dot, array, ndarray, vstack, transpose, sqrt
from numpy.linalg import solve, norm
def reflection(angle):
mat = array([[cos(2 * angle), -sin(2 * angle)],
[-sin(2 * angle), -cos(2 * angle)]])
"""A 2d reflection- / mirror- transformation
def func(x):
# why not use mat @ x???
Args:
angle (float): the angle of the line which mirrors the points.
Returns:
function(points): the function can be used to transform an array of points (2d)
"""
mat = array([[cos(2 * angle), -sin(2 * angle)], [-sin(2 * angle), -cos(2 * angle)]])
def _func(x):
# we do not use matrix-multiplication here because this is meant to work
# on an array of points
return dot(x, mat)
return func
return _func
def reflection3D(angle):
"""A 3d reflection- / mirror- transformation
Args:
angle (float): the angle of the line which mirrors the points. The transformation
happens in xy-plane.
Returns:
function(points): the function can be used to transform an array of points (3d)
"""
mat = array(
[
[cos(2 * angle), -sin(2 * angle), 0.0],
@@ -41,48 +57,85 @@ def reflection3D(angle):
]
)
def func(x):
# why not use mat @ x
return dot(x, mat)
def _func(points):
return dot(points, mat)
return func
return _func
def rotation(angle, midpoint=None):
midpoint = midpoint or [0.0, 0.0]
mat = array([[cos(angle), -sin(angle)], [sin(angle), cos(angle)]])
midpoint = array(midpoint)
vec = midpoint - dot(midpoint, mat)
def rotation(angle, center=None):
"""A 2d rotation - transformation
Args:
angle (float): the angle of the rotation.
center (2d array):
Returns:
function(points): the function can be used to transform an array of points (3d)
"""
center = center or [0.0, 0.0]
mat = array([[cos(angle), sin(angle)], [-sin(angle), cos(angle)]])
center = array(center)
vec = center - dot(center, mat)
trans = translation(vec)
def func(xx):
return trans(dot(xx, mat))
def _func(points):
return trans(dot(points, mat))
return func
return _func
def rotation3D(angle):
"""A 3d rotation - transformation
Args:
angle (float): the angle of the line which mirrors the points. The transformation
happens in xy-plane.
Returns:
function(points): the function can be used to transform an array of points (3d)
"""
mat = array(
[[cos(angle), -sin(angle), 0.0], [sin(angle), cos(angle), 0.0], [0.0, 0.0, 1.0]]
[[cos(angle), sin(angle), 0.0], [-sin(angle), cos(angle), 0.0], [0.0, 0.0, 1.0]]
)
def func(xx):
return dot(xx, mat)
def _func(points):
return dot(points, mat)
return func
return _func
def translation(vec):
def trans(x):
return [x[0] + vec[0], x[1] + vec[1]]
def translation(vector):
"""A 2d translation - transformation
def func(x):
return array(list(map(trans, x)))
Args:
angle (float): the angle of the line which mirrors the points. The transformation
happens in xy-plane.
return func
Returns:
function(points): the function can be used to transform an array of points (3d)
"""
def _trans(point):
return [point[0] + vector[0], point[1] + vector[1]]
def _func(points):
return array(list(map(_trans, points)))
return _func
def trim(p1, p2, p3, p4):
""" a trim function, needs to be documented
Args:
p1 (array or list of length 2): _description_
p2 (array or list of length 2): _description_
p3 (array or list of length 2): _description_
p4 (array or list of length 2): _description_
Returns:
_type_: _description_
"""
a1 = array(p1)
a2 = array(p2)
a3 = array(p3)
@@ -117,6 +170,16 @@ def trim(p1, p2, p3, p4):
def trimfunc(l1, l2):
"""seems like a trimm function, but I don't have any clue what it does,
sry ;)
Args:
l1 (_type_): _description_
l2 (_type_): _description_
Returns:
_type_: _description_
"""
ik = 0
i0 = array(l1[0])
for i in array(l1[1:]):
@@ -141,19 +204,36 @@ def trimfunc(l1, l2):
return False
def diff_norm(vec1, vec2):
vec = array(vec2) - array(vec1)
return norm(vec)
def diff_norm(vector_1, vector_2):
"""_summary_
Args:
vector_1 (np.array or list): the first vector
vector_2 (np.array or list): the second vector
Returns:
float: the length of the distance between the two vectors
"""
return norm(array(vector_2) - array(vector_1))
def nearestpts(evolv, underc):
def nearestpts(involute, undercut):
"""finds the closest points of a involute and an undercutut
Args:
involute (array or list of 2d points ?): the involute section of the tooth
undercut (array or list of 2d points ?): the undercut section of the tooth
Returns:
list of arrays: ????
"""
ik = 0
iout = 0
jout = 0
outmin = 1000.0
for i in array(evolv[1:]):
for i in array(involute[1:]):
jk = 0
for j in array(underc[1:]):
for j in array(undercut[1:]):
l = diff_norm(i, j)
if l < outmin:
re = diff_norm(i, [0, 0])
@@ -163,25 +243,46 @@ def nearestpts(evolv, underc):
iout, jout = [ik, jk]
jk += 1
ik += 1
return [vstack([underc[:jout], evolv[iout]]), evolv[iout:]]
return [vstack([undercut[:jout], involute[iout]]), involute[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 /= 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
def intersection_line_circle(point_1, point_2, radius):
"""return the intersection point of a line from point_1 to point_2 and a sphere of radius 1 and
midpoint 0,0,0
Args:
point_1 (_type_): start of line
point_2 (_type_): end of line
radius (float): the radius of the sphere
Returns:
_type_: _description_
"""
diff = point_2 - point_1
diff /= norm(diff)
p_half = diff.dot(point_1)
q = point_1.dot(point_1) - radius ** 2
t = -p_half + sqrt(p_half ** 2 - q)
return point_1 + diff * t
def arc_from_points_and_center(p_1, p_2, m):
"""return 3 points (x1, x12, x2) which can be used to create the arc"""
r = (norm(p_1 - m) + norm(p_2 - m)) / 2
p_12l = (p_1 + p_2) / 2
v = p_12l - m
def arc_from_points_and_center(point_1, point_2, center):
"""
returns 3 points (point_1, point_12, point_2) which are on the arc with
given center
Args:
point_1 (np.array with length 2): the start point of the arc
point_2 (np.array with length 2): the end point of the arc
center (np.array with length 2): the center of the arc
Returns:
[point_1, point_12, point_2]: returns the input points + the computed point
which is on the arc and between the input points
"""
r = (norm(point_1 - center) + norm(point_2 - center)) / 2
p_12l = (point_1 + point_2) / 2
v = p_12l - center
v /= norm(v)
p_12 = m + v * r
return (p_1, p_12, p_2)
p_12 = center + v * r
return (point_1, p_12, point_2)

View File

@@ -16,8 +16,6 @@
# * *
# ***************************************************************************
from __future__ import division
from __future__ import division
from numpy import (
cos,
sin,
@@ -28,7 +26,6 @@ from numpy import (
array,
linspace,
transpose,
vstack,
sqrt,
)
from ._functions import rotation3D, reflection3D, intersection_line_circle
@@ -216,13 +213,13 @@ class BevelTooth(object):
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.angular_backlash / 2)
backlash_rot = rotation3D(-self.angular_backlash / 2)
xyz = backlash_rot(xyz)
return xyz
def points(self, num=10):
pts = self.involute_points(num=num)
rot = rotation3D(-pi / self.z / 2)
rot = rotation3D(pi / self.z / 2)
pts = rot(pts)
ref = reflection3D(pi / 2)
pts1 = ref(pts)[::-1]

View File

@@ -45,6 +45,7 @@ def compute_shifted_gears(m, alpha, t1, t2, x1, x2):
def d_root_inv(x):
return 1.0 / np.cos(x) - 1
# use scipy (sp.optimize.minimize(f, f0, df).x) here (as we depent on scipy anyways)
alpha_w = find_root(alpha, root_inv, d_root_inv)
dist = m * (t1 + t2) / 2 * np.cos(alpha) / np.cos(alpha_w)
return dist, alpha_w

View File

@@ -16,30 +16,29 @@
# * *
# ***************************************************************************
from __future__ import division
from numpy import cos, sin, arccos, pi, array, linspace, transpose, vstack
from ._functions import rotation, reflection
class CycloidTooth:
def __init__(self, z1=5, z2=5, z=14, m=5, clearance=0.25, backlash=0.00, head=0.0):
def __init__(self, num_teeth_1=5, num_teeth_2=5, num_teeth=14, m=5, clearance=0.25, backlash=0.00, head=0.0):
self.m = m
self.z = z
self.num_teeth = num_teeth
self.clearance = clearance
self.backlash = backlash
self.z1 = z1
self.z2 = z2
self.num_teeth_1 = num_teeth_1
self.num_teeth_2 = num_teeth_2
self.head = head
self._calc_gear_factors()
def _calc_gear_factors(self):
self.d1 = self.z1 * self.m
self.d2 = self.z2 * self.m
self.d1 = self.num_teeth_1 * self.m
self.d2 = self.num_teeth_2 * self.m
self.phi = self.m * pi
self.d = self.z * self.m
self.d = self.num_teeth * self.m
self.da = self.d + 2 * (1 + self.head) * self.m
self.di = self.d - 2 * (1 + self.clearance) * self.m
self.phipart = 2 * pi / self.z
self.phipart = 2 * pi / self.num_teeth
self.angular_backlash = self.backlash / (self.d / 2)
def epicycloid_x(self):
@@ -111,7 +110,7 @@ class CycloidTooth:
pts_outer = transpose([pts_outer_x, pts_outer_y])
pts_inner = transpose([pts_inner_x, pts_inner_y])
pts1 = vstack([pts_inner[:-2], pts_outer])
rot = rotation(self.phipart / 4 - self.angular_backlash / 2)
rot = rotation(-self.phipart / 4 + self.angular_backlash / 2)
pts1 = rot(pts1)
ref = reflection(0.0)
pts2 = ref(pts1)[::-1]
@@ -121,9 +120,9 @@ class CycloidTooth:
def _update(self):
self.__init__(
m=self.m,
z=self.z,
z1=self.z1,
z2=self.z2,
num_teeth=self.num_teeth,
num_teeth_1=self.num_teeth_1,
num_teeth_2=self.num_teeth_2,
clearance=self.clearance,
backlash=self.backlash,
head=self.head,

View File

@@ -16,7 +16,6 @@
# * *
# ***************************************************************************
from __future__ import division
from numpy import (
tan,
cos,
@@ -44,7 +43,7 @@ class InvoluteTooth:
def __init__(
self,
m=5,
z=15,
num_teeth=15,
pressure_angle=20 * pi / 180.0,
clearance=0.12,
shift=0.5,
@@ -53,11 +52,22 @@ class InvoluteTooth:
backlash=0.00,
head=0.00,
properties_from_tool=False,
axle_hole=False,
axle_holesize=10,
offset_hole=False,
offset_holesize=10,
offset_holeoffset = 5,
):
self.pressure_angle = pressure_angle
self.beta = beta
self.m_n = m
self.z = z
self.num_teeth = num_teeth
self.undercut = undercut
self.axle_hole = axle_hole
self.axle_holesize = axle_holesize
self.offset_hole = offset_hole
self.offset_holesize = offset_holesize
self.offset_holeoffset = offset_holeoffset
self.undercut = undercut
self.shift = shift
self.clearance = clearance
@@ -77,12 +87,12 @@ class InvoluteTooth:
self.pitch = self.m * pi
self.c = self.clearance * self.m_n
self.midpoint = [0.0, 0.0]
self.d = self.z * self.m
self.dw = self.m * self.z
self.d = self.num_teeth * self.m
self.dw = self.m * self.num_teeth
self.da = self.dw + 2.0 * self.m_n + 2.0 * (self.shift + self.head) * self.m_n
self.df = self.dw - 2.0 * self.m_n - 2 * self.c + 2.0 * self.shift * self.m_n
self.dg = self.d * cos(self.pressure_angle_t)
self.phipart = 2 * pi / self.z
self.phipart = 2 * pi / self.num_teeth
self.undercut_end = sqrt(-(self.df**2) + self.da**2) / self.da
self.undercut_rot = (
@@ -110,7 +120,7 @@ class InvoluteTooth:
self.m / (self.d) * (pi / 2 + 2 * self.shift * tan(self.pressure_angle_t))
)
self.involute_rot2 = (
1 / self.z * (pi / 2 + 2 * self.shift * tan(self.pressure_angle_t))
1 / self.num_teeth * (pi / 2 + 2 * self.shift * tan(self.pressure_angle_t))
)
self.involute_rot = self.involute_rot1 + self.involute_rot2
self.angular_backlash = self.backlash / (self.d / 2)
@@ -126,7 +136,7 @@ class InvoluteTooth:
y = array(list(map(fy, pts)))
xy = transpose([x, y])
rotate = rotation(
self.undercut_rot + self.phipart / 2 - self.angular_backlash / 2
-self.undercut_rot - self.phipart / 2 + self.angular_backlash / 2
)
xy = rotate(xy)
return array(xy)
@@ -137,7 +147,7 @@ class InvoluteTooth:
x = array(list(map(fx, pts)))
fy = self.involute_function_y()
y = array(list(map(fy, pts)))
rot = rotation(self.involute_rot - self.angular_backlash / 2)
rot = rotation(-self.involute_rot + self.angular_backlash / 2)
xy = rot(transpose(array([x, y])))
return xy
@@ -169,10 +179,6 @@ class InvoluteTooth:
one_tooth = [u1, e1, [e1[-1], e2[0]], e2, u2]
return one_tooth
def gearfunc(self, x):
rot = rotation(2 * x / self.dw, self.midpoint)
return rot
def undercut_function_x(self):
def func(psi):
return cos(psi - (self.df * tan(psi)) / self.dw) * sqrt(
@@ -211,7 +217,7 @@ class InvoluteRack(object):
def __init__(
self,
m=5,
z=15,
num_teeth=15,
pressure_angle=20 * pi / 180.0,
thickness=5,
beta=0,
@@ -224,7 +230,7 @@ class InvoluteRack(object):
self.pressure_angle = pressure_angle
self.thickness = thickness
self.m = m
self.z = z
self.num_teeth = num_teeth
self.beta = beta
self.head = head
self.clearance = clearance
@@ -252,8 +258,8 @@ class InvoluteRack(object):
]
teeth = [tooth]
trans = translation([0.0, pitch, 0.0])
for i in range(self.z - 1):
if self.simplified and i > 3 and i < (self.z - 6):
for i in range(self.num_teeth - 1):
if self.simplified and i > 3 and i < (self.num_teeth - 6):
tooth = trans(tooth).tolist()
else:
tooth = trans(tooth).tolist()
@@ -263,7 +269,7 @@ class InvoluteRack(object):
teeth[-1].pop()
teeth[-1][-1][0] = 0
teeth[-1][-1][1] -= a / 2
if self.simplified and (i == self.z - 6):
if self.simplified and (i == self.num_teeth - 6):
teeth[-1].pop(0)
teeth[-1].pop(0)
teeth[-1][0][0] = 0

Some files were not shown because too many files have changed in this diff Show More