Skip to content

Keep curvature calculations finite for valid road geometry - #101

Merged
pfeiferj merged 3 commits into
pfeiferj:mainfrom
FrogAi:codex/keep-curvature-geometry-finite
Sep 7, 2026
Merged

Keep curvature calculations finite for valid road geometry#101
pfeiferj merged 3 commits into
pfeiferj:mainfrom
FrogAi:codex/keep-curvature-geometry-finite

Conversation

@FrogAi

@FrogAi FrogAi commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Straight and nearly straight road points can produce nonfinite curvature in the upstream calculation. Keep internal distances in float64, calculate the curve angle with a bounded asin, and give a zero-curvature segment its endpoint span so it contributes to smoothing.

Use sorted-side Heron arithmetic to limit cancellation near a straight line, and clamp the haversine term's upper rounding error before the square roots. The public distance interface still returns float32. Duplicates retain zero numeric fields at the middle point.

Road-connection decisions use curvature in m^-1. Keeping these values finite preserves valid connections and the weights used by smoothing and target-speed calculation. This is a numerical correction at the shared geometry owner; downstream policy is unchanged.

Case Verified behavior with this change
Straight-span fixture Zero curvature and a finite endpoint span that contributes to smoothing
Exact antipodal public-distance fixture below Finite distance and curve values
Nearly straight fixture below Curvature 2.885660024e-7 m^-1, unchanged on reversal

Antipodal endpoints test the public coordinate domain; they are not realistic road segments. Stable area arithmetic does not remove all float64 distance rounding, and this PR does not claim universal coordinate accuracy, a measured driving benefit, or a timing improvement.

Numerical rationale and reproducible validation

The product of the four sorted-side Heron factors is sixteen times area squared. Its square root therefore supplies the curvature numerator directly. The calculation keeps the nonnegative clamp, finite straight fallback and bounded asin. An arbitrary epsilon threshold would discard small valid curvature and is unnecessary here.

Tested source and validation scope

  • Upstream comparison: 7201c6b4b4ec1b0b9ea21daa8c05b80fdd7e01ee.
  • PR head: 9d61f06, tree 49fbfb3dfb3c59b956f314233274083b0e26d74d.

Checks described here used Go 1.25.1 on Linux amd64 with isolated fixtures and networking disabled during behavior tests. Existing repository tests remain unchanged; the extra reproductions below are deliberately outside the committed source. These are local execution results, not physical-device validation. The PR's Checks tab provides the published workflow result.

Focused checks exercised duplicates, ordinary circles, reversal, public float32 compatibility, connection thresholds 0.1/0.15/0.3 m^-1, smoothing and target-speed behavior. Repository tests and vet passed. The larger 3,004-triple side/coordinate-oracle diagnostics are supplementary local evidence; the complete public check below verifies the antipodal and nearly straight cases. The side oracle conditions on rounded computed distances; the coordinate oracle uses higher-precision spherical distances, so they answer different accuracy questions.

This compact check uses only the public API. Run it from a checkout containing the PR head below, with Linux Go 1.25.1 and its go.mod dependencies already available in the Go module cache. It copies source and dependencies to a temporary harness and does not execute the daemon. The source checkout is unchanged.

The public check passes at the PR head above. It tests finite results, the near-straight coordinate oracle and reversal stability. The coordinate-oracle tolerance includes remaining float64 distance rounding for this exact fixture.

ref=9d61f06a1288ec4ea6f74f7d56a3057444316a13 # Validated PR head.
scratch=$(mktemp -d)
mkdir -p "$scratch/source" "$scratch/tmp" "$scratch/gopath" "$scratch/gocache"
git rev-parse "$ref"
git archive "$ref" | tar -x -C "$scratch/source"
cp -a "$(go env GOMODCACHE)" "$scratch/modcache"
cat > "$scratch/source/math/pr101_reproduction_test.go" <<'GO'
package math_test

import (
  "math"
  "testing"
  geom "pfeifer.dev/mapd/math"
)

func TestPR101Reproduction(t *testing.T) {
  a := geom.NewPosition(23.013662427205986, -69.605449553279044)
  c := geom.NewPosition(-23.013662427205986, 110.39455044672096)
  curve := geom.CalculateCurvature(a, geom.NewPosition(0, 0), c)
  for _, value := range []float64{float64(a.DistanceTo(c)), curve.Curvature, curve.Angle, curve.ArcLength} {
    if math.IsNaN(value) || math.IsInf(value, 0) {
      t.Errorf("nonfinite antipodal value: %g", value)
    }
  }
  a = geom.NewPosition(-26.028568449441792, -175.9667291412151)
  b := geom.NewPosition(-26.02856844944178, -175.96672903544214)
  c = geom.NewPosition(-26.028568449441792, -175.9667188056291)
  curve = geom.CalculateCurvature(a, b, c)
  reverse := geom.CalculateCurvature(c, b, a)
  const coordinateOracle = 2.959785584661806e-7
  t.Logf("near-straight curvature=%.17g, reversed=%.17g", curve.Curvature, reverse.Curvature)
  if math.IsNaN(curve.Curvature) || math.Abs(curve.Curvature-coordinateOracle) > 1e-8 {
    t.Errorf("near-straight curvature %g; coordinate oracle %g, tolerance 1e-8", curve.Curvature, coordinateOracle)
  }
  if math.Abs(curve.Curvature-reverse.Curvature) > 1e-18 {
    t.Errorf("curvature changed on reversal")
  }
}
GO
cd "$scratch/source"
GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off GOMODCACHE="$scratch/modcache" \
  GOCACHE="$scratch/gocache" GOPATH="$scratch/gopath" GOTMPDIR="$scratch/tmp" TMPDIR="$scratch/tmp" \
  go test -mod=readonly -buildvcs=false -count=1 -run TestPR101Reproduction -v ./math

The larger local numerical check uses 3,004 triples (seed 101 plus four fixed cases), an 80-digit exact-computed-side Decimal oracle, and a separate 90-digit exact-coordinate spherical oracle. The full seeded/oracle and downstream harnesses are not attached to the public PR; this embedded snippet makes the two numerical checks independently reproducible without claiming public access to the larger local evidence package.

Combined validation: exact source tree a4c306906627db3ac7a8ab768651c8628d55465a combines #101 9d61f06a1288ec4ea6f74f7d56a3057444316a13, #103 20e7c25b054b6399360676a7f539a39b4fbf855c, #105 bfcfe77be066634e36054327b20cfa6541063b54, #107 8e5e677d1196838069e9665d4e9d962bcc1e116b, #116 6fd5bbd6cf617c24a7fefd5e302fd36688a1a63b, #136 30e8ce98ea7a4c8401dbb5bfc62120c84fc689e4. The only overlapping file is settings/download.go; the resolution retains #136's selected-row loop and #107's progress publication inside it.

Combined Linux amd64 tests (including the scratch regression fixtures), race checks, vet and build passed. Under ARM64 emulation, the existing Makefile build stage (make GO_CAPNP_PATH=/usr/local/go-capnp/std), committed repository tests, vet and both CLI help commands passed with Go1.25.1; go.mod/go.sum stayed unchanged and the resulting executable is AArch64. The ARM64 run does not include the extra amd64 scratch tests. It used an isolated retained build image, not a new dependency-install/image rebuild or physical device. No archive payload or live params were accessed. #105 still requires runtime-first rollout before regenerated tiles are distributed.

@FrogAi
FrogAi force-pushed the codex/keep-curvature-geometry-finite branch from d068322 to d7c5918 Compare August 10, 2026 02:54
@FrogAi
FrogAi force-pushed the codex/keep-curvature-geometry-finite branch from d7c5918 to b49258c Compare September 4, 2026 21:28
FrogAi added a commit to FrogAi/mapd that referenced this pull request Sep 4, 2026
Retain the original PR commits and the tested rewrite. The resulting
file tree is identical to b49258c.
Replace the earlier implementation with the simplified version.
@FrogAi
FrogAi force-pushed the codex/keep-curvature-geometry-finite branch from e5b55ac to 2a6c183 Compare September 4, 2026 21:50
@pfeiferj
pfeiferj merged commit f374b2b into pfeiferj:main Sep 7, 2026
1 check passed
@FrogAi
FrogAi deleted the codex/keep-curvature-geometry-finite branch September 8, 2026 00:10
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.

2 participants