Metadata-Version: 2.4
Name: ncgears
Version: 0.3.1
Summary: Generate noncircular gears with Python
Author: ncgears contributors
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/kylebme/ncgears
Project-URL: Issues, https://github.com/kylebme/ncgears/issues
Project-URL: Source, https://github.com/kylebme/ncgears
Keywords: gear,noncircular-gear,cad,mechanism,shapely
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: ezdxf<2,>=1.4
Requires-Dist: numpy>=1.26
Requires-Dist: scipy>=1.12
Requires-Dist: shapely>=2.1
Requires-Dist: sympy>=1.12
Provides-Extra: plot
Requires-Dist: matplotlib>=3.8; extra == "plot"
Requires-Dist: pillow>=10; extra == "plot"
Provides-Extra: test
Requires-Dist: pillow>=10; extra == "test"
Requires-Dist: pytest>=8; extra == "test"
Provides-Extra: build
Requires-Dist: build>=1.2; extra == "build"
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: matplotlib>=3.8; extra == "dev"
Requires-Dist: pillow>=10; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.9; extra == "dev"
Dynamic: license-file

# ncgears

`ncgears` generates noncircular gear pairs from a transmission law or a
pitch-curve shape.

The generator creates 2D outlines, verifies the assembled pair for interference
and contact-motion error, and exports CSV, SVG, DXF, JSON, PNG, and animated GIF
files. It supports closed gears, finite open segments, deep nonconvex pitch
curves, unequal ratios, and generalized-involute teeth.
The complete application and geometry pipeline are implemented in Python;
Shapely/GEOS provides robust floating-point polygon operations.

![Animated crazy_kidney_bean gear pair](docs/crazy_kidney_bean.gif)

> **Project status:** alpha. Generated geometry should be reviewed for the
> intended material, manufacturing process, load, speed, and tolerances.

## Install

```bash
pip install ncgears
```

PNG, animated GIF, and interactive Matplotlib previews are optional:

```bash
pip install "ncgears[plot]"
```

## Command line

Most functionality is available in the CLI:

```bash
ncgears "phi - 0.08*sin(2*phi)" --teeth 24 --module 1.5 \
  --name two_lobe --dxf two_lobe.dxf --render --gif --plot

ncgears "1 + 0.08*cos(2*phi)" --centrode --teeth 20 \
  --name centrode_two_lobe

# Offset every face inward by 0.04 module on each gear.
ncgears "phi - 0.08*sin(2*phi)" --clearance 0.04

# Or derive the offset for at most 1.5 degrees of driven-gear backlash.
ncgears "phi - 0.08*sin(2*phi)" --max-backlash 1.5
```

Run `ncgears --help` for all commonly used options.

## Basic python usage

Describe the desired relationship between the drive angle `phi` and the driven
angle. Here the driven gear speeds up and slows down twice per revolution while
returning to the same 1:1 average ratio:

```python
import ncgears

pair = ncgears.generate(
    "phi - 0.08*sin(2*phi)",
    teeth=24,
    module=1.5,
    name="two_lobe",
)

print(pair.summary())
pair.export_dxf("two_lobe.dxf")
pair.export_svg("two_lobe.svg")
```

DXF outlines are simplified by default to a maximum deviation of `0.001`
module from the full-resolution generated path. Override this in module units
with `pair.export_dxf("two_lobe.dxf", max_error=0.0005)` or
`--dxf-max-error 0.0005`; use zero to export every generated point.

`module` and all exported coordinates use millimetres. The returned
`GearPair` also provides:

```python
pair.drive_outline           # (N, 2) NumPy array
pair.driven_outline          # centered on its own shaft
pair.placed_driven_outline   # translated into assembled position
pair.center_distance
pair.drive_teeth
pair.driven_teeth
pair.ratio
pair.maximum_transmission_error
pair.clearance                 # per-face inward offset / module
pair.minimum_backlash_deg      # total driven-gear angular free play
pair.maximum_backlash_deg
pair.metadata                 # complete verification report
pair.directory                # CSV and JSON source files
pair.render()                 # pair.png; requires ncgears[plot]
pair.render_gif()             # pair.gif; follows the generated motion law
pair.plot()                   # interactive motion slider, zoom, and pan
```

Pass `plot=True` to `generate()` or `generate_from_centrode()` to open the
interactive plot as soon as generation finishes. The returned Matplotlib
figure can also be embedded or customized without opening a window:

```python
figure = pair.plot(show=False)
figure.suptitle("My mechanism")
```

The output directory defaults to `out/<name>/`. Each successful generation
contains `drive.csv`, `driven.csv`, `metadata.json`, and the sampled input.

## Clearance and backlash

Pass `clearance` to offset both finished gear solids inward, normal to every
face. The value is dimensionless: `clearance=0.04` offsets each gear by
`0.04 * module`, producing twice that normal separation between a mating pair
of faces.

Alternatively, pass `max_backlash_deg` to specify the maximum conventional
backlash: the total driven-gear rotation between contact on opposing flanks.
For a noncircular pair the local driven pitch radius changes, so backlash also
changes. ncgears chooses the face offset at the minimum driven pitch radius and
reports both the requested maximum and the resulting minimum:

```python
pair = ncgears.generate(
    "phi - 0.08*sin(2*phi)",
    max_backlash_deg=1.5,
)
print(pair.minimum_backlash_deg, pair.maximum_backlash_deg)
```

Conjugacy, protected-flank contact, and transmission error are verified before
the intentional offset. The delivered outlines are then independently checked
for validity, a single connected boundary, strict inward containment, retained
tip thickness, and sampled assembled-pair interference. These stages and the
resolved physical offset are recorded in `metadata.json`.

## Start from a pitch curve

If the drive gear's pitch radius is easier to describe than its motion law, use
a centrode expression:

```python
pair = ncgears.generate_from_centrode(
    "1 + 0.08*cos(2*phi)",
    teeth=20,
    module=1.0,
    name="centrode_two_lobe",
)
```

The radius may use arbitrary units; ncgears scales its arc length to the
requested tooth count and module. By default it solves the center distance for
one mate revolution. A specific ratio can be selected with
`target_cycle_delta`. For example, a five-lobed 5:2 angular ratio uses:

```python
import math

pair = ncgears.generate_from_centrode(
    "1 + 0.08*cos(5*phi)",
    teeth=100,
    target_cycle_delta=5 * math.pi,
    name="five_to_two",
)
```

Every input uses the hybrid analytical involute engine. It evaluates the
straight-rack flank and rounded rack-tip envelope equations without constructing
or sweeping a complete rack solid, so remote rack material cannot erase a
pitch-curve concavity. Exact addendum and dedendum offsets complete each tooth.
When a cutter-generated root is needed, the engine traces the two addendum
vertices of each opposing tooth instead of intersecting complete gear solids at
hundreds of discrete poses. Penetrating trajectory spans are extended beyond
their stock intersections, adaptively tessellated, closed, and subtracted only
inside analytic root regions. After the raw cut, only the exposed flank segment
connected to each addendum tip remains guarded, so a crossed root-side segment
cannot survive as a narrow material sliver.
Finite open profiles clip each analytical curve in rolling-arc parameter space,
then follow one quarter of the centrode back across the inner boundary.
Source-domain padding is used only to solve endpoint teeth; it cannot change or
clip the finished body.

## Closed and open designs

Closed gears require a smooth, strictly increasing motion whose cycle advance
produces an integer mate tooth count. A simple 2:1 pair is:

```python
pair = ncgears.generate("2*phi", teeth=20)  # 20 drive teeth, 10 driven teeth
```

Finite, non-repeating motion can be generated as an open segment:

```python
pair = ncgears.generate(
    "1.8*phi + 0.03*sin(phi)",
    open_=True,
    drive_end=2.4,
    teeth=12,
    name="finite_segment",
)
```


## What is verified

The Python engine uses analytic generalized-involute branches for every gear,
including finite open profiles and nonconvex centrodes.
Shapely/GEOS nodes the exact flank, rack-tip fillet, addendum, and dedendum
curves and arranges complete tooth bodies. Open bodies use an ordered,
parameter-clipped analytical boundary rather than a radial sector
intersection. A successful result includes checks for:

- one precision-stable connected closed outline per gear, with hub-connected bodies
- sampled whole-cycle solid interference
- contact motion recovered from the finished outlines
- analytic envelope/tangency, intersection, join, and chord residuals
- cusp-free, exposed protected flanks with sampled conjugate contact coverage
- smooth addendum-vertex undercut curves confined to analytic root regions
- root radius, tip thickness, and centrode curvature
- drive-outline fidelity to the requested centrode
- sliding-velocity and undercut diagnostics

`metadata.json` reports
`generation_backend: "hybrid_analytic_involute"`, flank sample count, maximum
envelope residual, maximum envelope-tangency residual, chord error, protected
flank diagnostics, outline-connectivity status, and addendum-vertex cutter
diagnostics. Pair verification uses a bounded thread pool of at most eight
workers.

These geometry checks are not load-rating or manufacturing certification.

## Development

```bash
python -m pip install -e ".[dev]"
python -m pytest
ruff check ncgears tests
python -m build
```

For magnified visual inspection of four representative roots on each gear:

```bash
python scripts/render_profile_zooms.py out/gear_pair
```

The GitHub Actions workflow tests Python 3.10–3.13, builds a platform-independent
ncgears wheel, and smoke-tests the installed wheel. Shapely supplies its GEOS
runtime through its own platform wheels.

## Method and prior work

The pitch-curve equations follow Uwe Bäsel,
["Determining the geometry of noncircular gears for given transmission
function"](https://arxiv.org/abs/1905.02642). Tooth geometry is constructed by
evaluating its analytic straight-rack envelope. GEOS performs curve arrangement
and solid regularization rather than project-specific intersection code. The
silhouette-fitting problem addressed by Xu et al.,
["Computational Design and Optimization of Non-Circular
Gears"](https://doi.org/10.1111/cgf.13939), is complementary: a fitted
transmission derivative or polar centrode can be passed into ncgears.

Contributions and reproducible test cases are welcome through the
[issue tracker](https://github.com/kylebme/ncgears/issues).

## Project context

This project contains entirely AI generated code. This project has been my personal benchmark for 
determining how capable coding models are for over a year. Models have saturated this benchmark, 
so I'm releasing the project as an alpha.

## License

ncgears is distributed under the Apache License 2.0.
