Skip to content

feat(g.Curve): add isLine() and use it in getSubdivisions() - #3504

Open
kumilingus wants to merge 1 commit into
clientIO:devfrom
kumilingus:feat/curve-is-line
Open

kumilingus wants to merge 1 commit into
clientIO:devfrom
kumilingus:feat/curve-is-line

Conversation

@kumilingus

Copy link
Copy Markdown
Contributor

Summary

Closes the @jointjs-dev board card "g: add g.Curve.isLine method".

  • g.Curve#isLine()true when both control points lie on the startend chord (the curve traces a straight line, possibly overshooting the chord). Point-like curves and loops (start equal to end) return false.
  • Tolerant to float noise. The cross product is compared against 1e-9 × chord² rather than exact zero. Since cross = chord length × distance of the control point from the chord, this is a scale-independent "distance below 1e-9 × chord length" test. Motivation: the exact-zero check misses 1913 / 2000 straight curves produced by Curve.throughPoints([a, b]) — the library's own straight-curve constructor — because of the (2·P0 + P3) / 3 arithmetic. With the tolerance: 0 / 5000 missed.
  • getSubdivisions() now calls isLine() for its "straight-line curve" special case (forces 2 × precision iterations, because the observed-length convergence test cannot distinguish iterations on a straight curve). Effects:
    • straight curves from throughPoints() get the intended 64 subdivisions (precision 3) instead of 4 → finer t ↔ length mapping for unevenly spaced control points;
    • loops whose start equals end no longer trigger the special case (they were matched by the exact check because cross(start, end) is trivially 0 when start === end).
  • No isPoint() alias (the card's suggestion); it would be !isDifferentiable() and no other shape has one.
  • No tolerance option — can be added later without breaking; no consumer needs it today.

Changesets: minor for the new method, patch for the getSubdivisions() behaviour change.

Test plan

  • karma:geometry — 353/353; new isLine() module (evenly/unevenly spaced, control points equal to endpoints, overshooting, diagonal; curved, one control point off, point, loop; 100 random throughPoints() curves, tiny coordinates, visible deviation rejected). Watched failing first (isLine is not a function). Existing getSubdivisions() assertions (64 for straight, 4 for curved) unchanged.
  • karma:joint — 2113/2113
  • grunt test:ts
  • eslint on all touched files

🤖 Generated with Claude Code

`isLine()` is true when both control points lie on the start-end chord.
The cross product is compared against a scale-relative tolerance
(1e-9 of the squared chord length) instead of exact zero, so straight
curves built by `Curve.throughPoints()` - which carry floating point
noise in their control points - are recognized. Point-like curves and
loops (start equal to end) are not lines.

`getSubdivisions()` previously inlined an exact-zero version of this
check for its "straight-line curve" special case. That check missed
~96% of straight curves produced by `throughPoints()` (falling back to
4 subdivisions instead of the intended 2^(2*precision)) and matched any
loop whose start equals its end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant