Skip to content

subtle cu2qu rounding issue #1981

@cmyr

Description

@cmyr

this is ultimately meaningless, but wanted to open an issue to at least track.

cu2qu epsilon causes extra point for near-degenerate cubics

Summary

fontc produces 1 fewer point than fontmake for the glyph ordmasculine in RubikGlitchPop. The difference comes from cubic-to-quadratic conversion of a nearly degenerate cubic where all four control points are within ~0.05 design units and round to the same integer coordinate.

Reproduction

python3 -m ttx_diff 'https://github.com/NaN-xyz/Rubik-Filtered?1126980434#glitchpop/sources/RubikGlitchPop.glyphs'

Only the glyf table differs: ordmasculine has 423 points (fontc) vs 424 points (fontmake).

The source curve

The cubic in question has four nearly-identical control points:

p0 = (234.740, 471.556)
p1 = (234.742, 471.574)
p2 = (234.744, 471.592)
p3 = (234.746, 471.610)

All round to (235, 472). The lines p0-p1 and p2-p3 are nearly parallel, producing a cross product of ~5e-16.

What happens

cu2qu's dot() function clamps values with abs() < 1e-15 to zero (cu2qu.py:58-59). This was added to ensure consistent results between pure Python and Cython runtimes, where IEEE-754 rounding can differ slightly for perpendicular vectors.

For this curve, the ~5e-16 cross product gets clamped to zero, causing a ZeroDivisionError in the crossing-point calculation. The n=1 quadratic approximation fails and falls through to n=2, producing 2 quadratic segments (an extra off-curve point) instead of 1.

kurbo (used by fontc) has no such epsilon — the ~5e-16 cross product is treated as non-zero, a valid crossing point is found, and 1 quadratic segment is produced.

Impact

The difference is purely cosmetic. Both outputs render identically — all points are at the same integer coordinate (235, 472). fontc's output is marginally better (1 fewer point).

Metadata

Metadata

Assignees

No one assigned

    Labels

    craterIssues based on a crater diff

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions