This document describes the current ncgears implementation: how a requested
motion becomes two pitch curves, how teeth are constructed on those curves, how
non-working root interference is removed, and what is verified before an outline
is returned. It also records the physical and numerical boundaries of that
implementation.
ncgears has one tooth-geometry engine. All inputs ultimately pass through the
same hybrid analytical generalized-involute pipeline. The distinction between a
transmission-law input and a centrode input exists only at the front of the
pipeline; both become the same sampled motion representation before geometry is
constructed.
The implementation is divided into a few deliberately narrow layers:
ncgears/api.pyparses expressions, differentiates them with SymPy, constructs closed or padded-open sample tables, and writes the source CSV.ncgears/engine.pyconverts those samples into pitch geometry, constructs and arranges the analytical tooth curves, removes cutter interference, and runs verification.ncgears/_policy.pycontains sampling densities, tolerances, topology limits, and public defaults. Dimensional tolerances in this file are factors of module.ncgears/result.pyloads the generated outlines and metadata and provides SVG and DXF export.ncgears/rendering.pyprovides optional previews and animation. Rendering is downstream of generation and does not alter the delivered geometry.
At a high level, generation follows this data flow:
flowchart TD
A["Transmission expression or drive-centrode expression"] --> B["Validate, differentiate, and sample"]
B --> C["Piecewise-quintic motion representation"]
C --> D["Pitch radii, center distance, and common rolling arc"]
D --> E1["Drive analytical tooth curves"]
D --> E2["Driven analytical tooth curves"]
E1 --> F["GEOS curve arrangement and initial solids"]
E2 --> F
F --> G["Opposing addendum-vertex undercut cuts"]
G --> H["Conjugate-geometry and whole-solid verification"]
H --> I["Optional global face clearance"]
I --> J["Final connectivity and interference verification"]
J --> K["Outline CSV and metadata JSON"]
The drive and driven analytical bodies are independent until the cutter and pair verification stages, so the engine constructs them concurrently when more than one worker is available. Phase verification also uses a bounded thread pool. The global worker limit is eight.
A transmission expression defines the driven angle psi as a function of drive
angle phi:
psi = psi(phi)
The API subtracts the value at the active start, so the stored motion begins at
zero without changing any derivative or geometry. SymPy evaluates psi and its
first three derivatives on a uniform grid. The derivative must remain finite and
strictly positive; the engine also rejects ratios outside its bounded numerical
range.
In closed mode, the first three derivatives must agree at the period endpoints. The cycle advance is
Delta_psi = psi(phi_0 + period) - psi(phi_0),
and the mate tooth count is constrained by
Z_driven = Z_drive * period / Delta_psi.
That value must be integral. The closed sample count is rounded up to a multiple of the reduced tooth-ratio denominator so the sample grid and tooth cycles remain aligned. A second periodicity check is made after interpolation: shifting through one driven-body revolution must advance the motion by exactly one rigid driven revolution, with compatible first and second derivatives.
A centrode expression defines an unscaled drive pitch radius r(phi). It must be
positive and finite. Closed inputs must match in value, first derivative, and
second derivative at the period endpoints.
The radius and an auxiliary reference center distance C_ref define a motion
derivative:
psi1 = r / (C_ref - r)
psi2 = C_ref * r' / (C_ref - r)^2
psi3 = C_ref * (r'' / (C_ref - r)^2 + 2 * r'^2 / (C_ref - r)^3)
If C_ref is not supplied, the engine solves for it with a bracketed root search
so that
integral(r / (C_ref - r), phi) = target_cycle_delta.
The solved derivative is numerically integrated to obtain psi. The original
centrode units are only a shape and ratio reference. The common geometry stage
later scales the resulting pitch length to the requested tooth count and module.
Transmission CSV input contains phi, psi, psi1, psi2, and psi3.
Geometry uses one uniform piecewise-quintic Hermite series constrained by psi,
psi1, and psi2. Values and all evaluated derivatives through third order come
from that series, keeping position, tangent, curvature, and curvature derivative
consistent with one interpolant. The sampled psi3 column records the source
evaluation but is not an independent constraint on the motion series.
Each quintic segment matches value, first derivative, and second derivative at both ends. Periodic series wrap the final segment to the first while adding the cycle advance. Non-periodic series clamp evaluation to their available sampled domain.
An open result needs additional motion history to construct teeth at the active endpoints. Padding lengths are specified in circular pitches and converted to angular spans using the local pitch-arc density. The user's expression is evaluated only on the requested active interval. Outside that interval the API synthesizes a piecewise-quintic continuation that matches endpoint value, tangent, and second derivative.
For a transmission law, the continuation settles to the endpoint angular ratio, which approaches a circular pitch curve. For a direct centrode, it settles to an approximately constant radius. The transition span is limited to avoid an unbounded radial excursion. Padding supplies construction history only: it does not define or clip the finished open body.
Let q = d psi / d phi and let C be the physical shaft center distance. For an
external pair, the pitch radii are
r_drive(phi) = C * q / (1 + q)
r_driven(phi) = C / (1 + q).
They sum to C at every pose. The drive-centrode arc density per unit center
distance is
rho(phi) = hypot(psi2, q * (1 + q)) / (1 + q)^2.
With circular pitch p = pi * module, the center distance is selected so the
active drive pitch length holds the requested number of teeth:
C = Z_drive * p / integral(rho(phi), phi).
The integral is represented by a dense cumulative Simpson table. It provides
both arbitrary interval integrals and the inverse map from common rolling arc
s back to phi. Closed lookups wrap by complete arc cycles; open lookups must
remain inside the padded motion domain.
In the local complex coordinate systems used by the engine, the pitch points are
P_drive(phi) = r_drive(phi) * exp(-i * (phi - phi_0))
P_driven(phi) = -r_driven(phi) * exp( i * (psi(phi) - psi(phi_0))).
The initial drive center is the origin. The driven outline is also generated
about its own origin and is translated by C when assembled. Unit tangents are
computed analytically from psi1 and psi2; oriented pitch-curve curvatures use
derivatives through psi3.
The common rolling arc is the central parameter of tooth generation. A distance
p in s is one tooth pitch on both members. This avoids placing teeth by polar
angle, which would be wrong on a noncircular or nonconvex centrode.
For tooth phase k, flank sign sigma in {-1, +1}, pressure angle alpha,
pitch p, pitch point P(s), and pitch tangent T(s), define the rolling rack
coordinate
lambda(s) = sigma * p / 4 - (s - k * p).
The generalized-involute working flank is evaluated directly as
X_flank(s) = P(s)
+ lambda(s) * T(s)
* (cos(alpha) + i * sigma * sin(alpha)) * cos(alpha).
The member-specific pitch frame supplies the correct drive or driven orientation. This is the analytical envelope of a straight rack flank, not an approximation obtained by subtracting many sampled rack positions.
Addendum and dedendum curves are parallel offsets of the pitch curve:
X_offset(s, h) = P(s) + h * N(s),
where the outward unit normal N is selected for the member's orientation,
h = addendum gives the tooth tip, and h = -dedendum gives the root boundary.
The root fillet is another analytical envelope branch. The engine follows the
center of the rack-tip circle at dedendum - fillet_radius, then offsets that
center by the fillet radius along the normalized local envelope normal. The
branch is selected continuously from exact dedendum contact, including across
pitch-curve inflections. It is not a circular arc fitted to the finished polygon
and not a post-processing blend.
The nominal flank-to-fillet contact and fillet-to-dedendum contact follow from the rack geometry. The addendum end of each flank is found by intersecting the continuous flank parameterization with the continuous addendum offset. GEOS intersects dense sampled curves to produce robust candidates; SciPy least-squares then refines the two curve parameters and measures the continuous residual. The same candidate-and-refinement pattern is used for nontrivial flank/fillet intersections.
A retained working flank must be regular. For the oriented pitch curvature
kappa, a singular point satisfies the per-flank cusp equation
lambda * kappa - sigma * tan(alpha) = 0.
The engine searches the entire candidate working interval. It brackets sign changes and refines them with Brent's method. Because an even-multiplicity cusp does not change sign, sampled local minima of the absolute equation are also minimized and accepted when their residual is sufficiently small. The grid is repeatedly doubled until its root set stabilizes or reaches the policy limit.
If no cusp lies between the nominal rack-tip contact and the addendum, the analytic fillet is joined at its nominal tangent contact. If a cusp exists, the engine searches for a nontrivial regular intersection between the flank and the fillet and keeps the tip-side cusp-free component. If no such closure exists, it stops just beyond the cusp and inserts a sacrificial connection to close the initial stock. That case is a hybrid undercut: the connection is non-working material that the opposing gear's addendum-vertex trajectory must remove later.
Metadata distinguishes flanks classified as analytically undercut, hybrid cases,
and the eventual root closure with analytic_undercut_count,
hybrid_undercut_count, cutter_undercut_curve_count, and
fillet_closure_mode.
Flank, fillet, addendum, dedendum, backing, and cutter paths all have continuous parameterizations. The engine samples them for intersection-candidate discovery, cusp and cutter-penetration searches, GEOS arrangement and Boolean operations, verification, and export. Candidate flank/offset and flank/fillet intersections are subsequently refined against the continuous parameterizations. Cutter stock penetration intervals remain point-sampled and are padded before subtraction.
For each interval, the engine compares the exact curve midpoint with its nearest
point on the endpoint chord. It doubles the interval count until the maximum
midpoint-to-chord error is within the module-scaled target or the sample ceiling
is reached. Working flanks start with a denser minimum grid than closure curves.
The ordinary analytical curve target is 0.000025 * module; finished cutter
trajectories use the finer 0.0000025 * module target.
The delivered outline is therefore a tessellated polygon even though its source curves and intersection refinements are analytical.
A closed member begins with material inside the analytical dedendum offset, unioned with a small central support disk so the intended root body remains hub connected. Each tooth is then assembled in boundary order from:
- one rounded rack-tip fillet,
- one generalized-involute flank,
- the addendum offset between the two flank tips,
- the opposing generalized-involute flank,
- the opposing fillet, and
- the dedendum offset back to the first root.
GEOS validates and unions these tooth polygons with the root blank. Separate root-trim polygons form the intended cutter mask: the engine unions the pitch material with per-root closure regions, intersects that union with the retained body, and later adds a one-sided protected-flank guard. Working flanks and addendum material are therefore not removed merely because a cutter trajectory crosses them.
GEOS may return several polygonal pieces while regularizing a curve arrangement. The implementation retains the largest piece. Connected-loop checks apply to that retained outline, while the closed-drive centrode-fidelity and minimum-area checks and the subsequent pair checks catch broad losses of intended material. They do not prove that no smaller fragment was discarded during regularization.
An open member is not made by building a padded closed solid and intersecting it with radial sector faces. Such an intersection is generally wrong for a noncircular pitch curve.
Instead, the engine walks one ordered boundary and clips every flank, fillet, addendum, and dedendum curve in common rolling-arc space to the active interval. Straight endpoint seams connect that outer boundary to an inner curve at one quarter of the centrode radius; the inner curve then returns from the active end to the active start. Open tooth phases are offset by one half pitch, centering the requested teeth between the active arc limits. Every constituent curve is still clipped to those limits, so the endpoint seams—not construction padding—complete the finite body.
Either member may span one complete body revolution. A span greater than one revolution is rejected because one open boundary could no longer represent it as a single non-self-overlapping body.
An open output is intended only for its finite active operating range. It must not be treated as a continuously rotating closed pair.
No complete rack solid is swept through either gear. This is important for deep nonconvex centrodes: a remote part of a fictitious rack can pass through a concavity and erase valid material unrelated to the local tooth being cut.
The initial analytical body instead supplies exact working flanks and the preferred rounded root closures. Remaining interference is removed with the two vertices of every opposing addendum edge:
- Every opposing addendum vertex is transformed through the cutter search interval in each target gear's local frame: the complete cycle for a closed pair, or the active interval plus endpoint search padding for an open pair.
- A point-in-polygon search finds consecutive parameter intervals during which that vertex penetrates the initial target stock. A vertex that stays inside for an entire closed cycle is rejected as an invalid pair.
- Penetration depth is measured against the target boundary. Excursions no deeper than the ordinary analytical chord-error budget are treated as tessellation noise and ignored.
- Each significant interval is extended by one quarter of a circular pitch at both ends, limited by the available input domain.
- The continuous trajectory is adaptively tessellated, closed into a cutter region, and dilated by the requested root clearance plus a small Boolean allowance.
- The cut is intersected with both the initial solid and that member's analytic root-trim zone before subtraction.
For finite open profiles, the search covers one circular pitch beyond each requested endpoint using the construction padding already required for endpoint teeth. If a penetration interval reaches the finite search edge, a narrow local strip around the open trajectory closes the cut without reconstructing a complete cutter solid or requiring more user-defined motion.
Before final subtraction, the raw cut is used to discover which portion of every protected flank remains exposed and connected to its addendum tip. Only that tip-connected component remains a working flank. A one-sided guard is built on the material side of it and subtracted from the cutter region. This retains the flank up to its cutter intersection without leaving the doubled-back root sliver that a symmetric buffered-flank guard would create.
Cuts are unioned per member, subtracted, and precision-normalized to collapse near-coincident Boolean seams. The finished boundaries are then re-extracted and required to remain one precision-stable closed loop.
Root clearance and global clearance occur at different stages and have different effects.
root_clearance expands only the addendum-vertex cutter regions before they are
subtracted. It does not offset the working-flank faces. If an enlarged cut reaches
the root-side end of a protected span, that span is shortened to its exposed
tip-connected component; the remaining analytical component is guarded without
being offset.
Global clearance is a per-face normal offset. After all zero-clearance
conjugacy, protected-contact, and transmission-error checks pass, each complete
solid is eroded inward with a constant-distance GEOS buffer. Re-entrant joins are
rounded; convex corners remain intersections of their translated faces. The
result must be one valid body, lie strictly inside the original, pass the
rack-derived analytical tip-thickness guard, and remain interference-free over
the sampled motion. The engine does not geometrically measure minimum tip
thickness on the post-offset polygon.
If maximum driven-gear backlash beta_max is requested instead, the engine
chooses the same per-face distance c at the minimum driven pitch radius:
c = beta_max * r_driven_min * cos(alpha) / 4
beta(phi) = 4 * c / (r_driven(phi) * cos(alpha)).
Angles in those equations are in radians. Metadata reports the resulting minimum and maximum total driven angular free play. Explicit global clearance and maximum backlash are mutually exclusive.
Generation is fail-closed: a result is returned only after its analytical curves, polygon topology, protected working geometry, and assembled motion pass the implemented checks. Those checks combine continuous residual evaluation with dense sampled solid tests; they are not a formal proof over every real-valued phase.
The engine rejects a result when any enforced condition falls outside its tolerance. These conditions include:
- straight-rack envelope and envelope-tangency residuals,
- continuous curve-intersection residuals,
- rack-tip-fillet contact with the dedendum,
- adaptive midpoint-to-chord error,
- cusp-free regularity of every retained working flank,
- exposure of protected analytical flanks on the finished polygon boundary,
- precision-noded single-loop connectivity,
- positive rack-derived tooth-tip thickness after requested global clearance,
- closed drive-outline fidelity to the requested centrode, and
- a broad closed-body area check against the drive pitch area.
Other values are diagnostics rather than independent rejection criteria. The engine reports the minimum outline radius, sampled pitch-curve curvature, and a centrode-convexity classification without applying acceptance thresholds to them. Reported pre/post-clearance tip thickness is derived from the rack geometry and requested normal offset; it is not measured from the finished polygon.
The corresponding metadata includes:
maximum_envelope_residualmaximum_envelope_tangency_residualmaximum_intersection_residualmaximum_fillet_root_residualmaximum_analytic_chord_errorminimum_flank_regular_factormaximum_protected_flank_boundary_errormaximum_protected_contact_residual
Whole-solid interference is evaluated at four staggered phase grids. Closed grids contain at least 64 base phases and at least four phases per tooth of the larger member; open grids contain at least 48 base phases. Offsets of zero, one half, one quarter, and three quarters of a grid cell reduce the chance that an error aligns with the primary phase grid. Polygon overlap area at every sampled pose must remain below the module-scaled GEOS allowance.
Protected analytical flank pairs are independently matched in common-arc space and transformed into the assembled pose. The engine reports the number of matching pairs, their maximum residual, and the fraction of sampled phases with at least one candidate. It rejects the pair if no sampled phase has a protected contact candidate, but it does not require candidate coverage at every phase; whole-solid contact recovery supplies the separate positive-contact check.
At several additional off-grid poses, selected with a non-dyadic phase offset,
the engine searches for the first positive solid overlap on both sides of the
requested driven angle. It expands the angular search and then bisects the first
contact transition. The largest required correction is reported as
maximum_transmission_error. This recovers contact from the finished tessellated
solids rather than assuming that source-curve conjugacy alone guarantees contact
after arrangement.
All contact and transmission-error measurements are made before intentional global clearance, because clearance deliberately separates the members at the nominal pose. After clearance, the engine repeats connectivity, containment, artifact, and sampled interference checks instead.
Conjugacy and surface sliding are different quantities. For an external pair,
the relative sliding speed at contact point Q is proportional to
(1 + psi1) * distance(Q, P),
where P is the instantaneous pitch point. A generalized involute pair therefore
has zero sliding only as contact crosses the pitch point. Metadata reports a
conservative sliding-velocity factor as well as the angular correction at which
the tessellated finished solids establish contact.
A successful generation writes the sampled source (transmission.csv or
centrode.csv), counter-clockwise closed drive.csv and driven.csv outlines,
and metadata.json. Each outline is expressed about its own shaft center; result
and export code places the driven member at the reported center distance for pair
views.
SVG export writes the delivered polygons directly. DXF export writes closed lightweight polylines and may simplify them with a separately specified module-scaled error. Neither export path participates in generation or verification. PNG, interactive Matplotlib, and GIF output are visualizations of the already generated pair.
The program either returns a pair that passes its implemented solid checks or rejects the requested motion and design parameters with a specific error. It does not imply that every scalar function is physically realizable by one pair of fixed-center external gears.
A closed external pair requires:
- a bounded, strictly positive transmission derivative,
- a sufficiently smooth motion law,
- rational cycle advance and integral drive/driven tooth counts,
- motion history compatible with both rigid gear revolutions,
- enough root material for a connected solid, and
- positive contact without solid interference.
Exact dwells, infinite ratios, ratio reversals, discontinuous velocity, irrational closed ratios, and incompatible driven-cycle histories require an open segment or a different mechanism topology.
Shapely/GEOS performs curve arrangement and Boolean operations in double precision. GEOS finds candidate intersections between sampled analytical curves, and SciPy refines them against the continuous parameterizations. Rounded rack-tip fillets are analytical envelope branches, not post-processing arcs.
All dimensional tolerances are factors of module defined in
ncgears/_policy.py; there is no hidden unit-sized tolerance floor. Very large
coordinate offsets, modules close to machine precision, or extremely thin
remnants remain unsuitable inputs.
The four staggered verification grids and additional off-grid contact recovery catch many grid-aligned errors, but whole-solid verification remains dense sampling rather than formal interval arithmetic over every real-valued phase.
The two sampling controls affect different stages. samples sets the source
expression grid and therefore the resolution of the piecewise-quintic motion
interpolant. It should be increased when the motion law has high-frequency
content or sharply changing derivatives. samples_per_radian sets the initial
density of analytical-curve tessellation before adaptive chord-error refinement.
Increasing samples_per_radian is appropriate when:
- the tooth scale is very small relative to center distance,
- high-frequency motion is already adequately resolved by
samplesbut its resulting geometry needs a denser initial curve grid, - the reported contact correction is too large, or
- manufacturing tolerance approaches the reported tessellation error.
Open bodies use exactly the same analytical flank, fillet, addendum, and dedendum curves as closed gears. Their finite boundary is clipped in rolling-arc space, not inferred from padding. The input expression only needs to be valid on the requested interval.
Construction padding, finite body closure, and endpoint cutter strips make an open segment geometrically self-contained, but they do not make it periodic. An open result is valid only over its requested active motion. Spans greater than one revolution of either body are unsupported.
The geometry report does not replace:
- Hertzian contact-stress analysis,
- tooth-root bending and fatigue analysis,
- elastic transmission-error analysis,
- lubrication and flash-temperature analysis,
- three-dimensional lead/crowning design,
- shaft, bearing, and housing deflection analysis, or
- manufacturing-process validation.
Those require load, material, face width, speed, lubrication, and tolerance inputs outside the present two-dimensional geometry contract.