tscircuit / tscircuit/math-utils

pointToSegmentClosestPoint loses finite projections at extreme coordinate scales

Open
#52 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
0
Forks
11
PR merge metrics
No merged PRs in 30d

Description

pointToSegmentClosestPoint can return NaN or the wrong endpoint for finite, nonzero segments when the squared length overflows or underflows. This is independent of the final distance-norm issue in #51: the closest-point API does not compute a distance.

Reproduced on main 7b26935a5cccb6b68a3d889d5e5d03ba04dc9a1b (v0.0.38), Bun 1.4.2. For a horizontal segment (0,0) -> (4*s,0) and query (2*s,3*s), the exact closest point is (2*s,0):

s actual x/s expected x/s
1 2 2
1e160 NaN 2
1e-170 0 2

Reversing the small segment changes the wrong answer to x/s=4. All input coordinates and the expected result are finite and representable. These are extreme-scale robustness cases, not a claim that ordinary PCB coordinates fail.

Runnable regression (tests/projection-scales-repro.test.ts, bun test tests/projection-scales-repro.test.ts):

import { expect, test } from "bun:test"
import { pointToSegmentClosestPoint } from "../src"

for (const scale of [1, 1e160, 1e-170]) {
  for (const reverse of [false, true]) {
    test(`closest point at ${scale}, reverse=${reverse}`, () => {
      const left = { x: 0, y: 0 }
      const right = { x: 4 * scale, y: 0 }
      const a = reverse ? right : left
      const b = reverse ? left : right
      for (const [x, expectedX] of [[2, 2], [-1, 0], [5, 4]]) {
        const actual = pointToSegmentClosestPoint(
          { x: x * scale, y: 3 * scale }, a, b,
        )
        expect(actual.x / scale).toBe(expectedX)
        expect(actual.y).toBe(0)
      }
    })
  }
}
test("zero-length segment remains a point", () => {
  const endpoint = { x: 2, y: 3 }
  expect(pointToSegmentClosestPoint({ x: 4, y: 7 }, endpoint, endpoint)).toEqual(endpoint)
})

Observed: 3 tests pass, 4 fail; TypeScript check passes. In src/segment-distance.ts, dx_ab * dx_ab + dy_ab * dy_ab becomes Infinity or zero. The former makes the interior projection Infinity/Infinity; the latter incorrectly selects the degenerate-segment branch. A scale-aware projection should retain interior projection and endpoint clamping without treating a nonzero segment as a point. Other helpers repeat the squared-length formula and may need the same treatment; this reproduction isolates the closest-point API. #44 focuses on distance performance rather than this function.

Prepared with Codex (Astra) assistance and verified locally.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/segment-distance.ts at pointToSegmentClosestPoint and run bun test tests/projection-scales-repro.test.ts. Check the reported extreme-scale cases, including reversed and zero-length segments. Done means the regression passes, finite nonzero segments retain the expected closest point, and the zero-length case remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
bun, typescript
Domain
testing-qa, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.