Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions docs/jts-1145-pip-lane-2026-08.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# PIP gallery lane — JTS #1145 YStripesPointInAreaLocator

**Date**: 2026-08-05.
**topic:** `relate` · epic **#67** (PIP / RelateNG consumers; JCT gallery as regression oracle)
**Upstream**: [locationtech/jts#1145](https://github.com/locationtech/jts/pull/1145)
*Implement `YStripesPointInAreaLocator`* (micycle1) — new indexed
point-in-area locator using Y-stripe segment buckets + DD ray-crossing
([tg POLYGON_INDEXING](https://github.com/tidwall/tg/blob/main/docs/POLYGON_INDEXING.md#ystripes)).
**NTS twin**: none yet; production baseline is
`IndexedPointInAreaLocator` / `SimplePointInAreaLocator`.
**Scope**: Run the corpus PIP gallery
([`nts-oracle-gallery.md`](nts-oracle-gallery.md)) as a **differential
soundness gate** against a faithful C# port of the PR tip. **No new Rocq
theorems.**

**Verdict**: **GREEN** — on **14** gallery vectors (vertex-graze, horizontal
edge, rect/pixel boundary, Spectre pocket, Hat pocket), the Y-stripes port
returns the **geometric correct answer** and agrees with NTS
`IndexedPointInAreaLocator` and `SimplePointInAreaLocator` on every row
(including OGC `BOUNDARY` where applicable). Safe soundness posture for
merge from the proofs-corpus gallery; recommend pinning the WKT cases into
`AbstractPointInRingTest` / a dedicated test on the Java side.

---

## §1 — What #1145 adds

| File | Role |
|---|---|
| `YStripesPointInPolygonLocator` | Per-ring Y-stripe index + ray-crossing (`locateYStripes`) |
| `YStripesPointInAreaLocator` | Multi-polygon STRtree of per-polygon locators; single-poly fast path |
| `YStripesPointInAreaLocatorTest` | Extends `AbstractPointInRingTest` |
| Perf / stress tests | Circle + perturbed grid |

Core locate path (per ring):

1. Envelope reject (`coversPointFast`).
2. Map `py` → stripe; scan only segments mapped to that stripe.
3. **Horizontal** edges (`ay == by`): never flip parity; `ON` iff `py == ay` and `px ∈ [xmin,xmax]`.
4. Segment entirely right of query (`px < xmin`): flip iff half-open y-straddle `(ay > py) != (by > py)`.
5. Segment entirely left (`px > xmax`): ignore.
6. Otherwise `raycast` via **`CGAlgorithmsDD.orientationIndex`**; collinear-in-bbox → `BOUNDARY`; orient after normalizing edge direction → cross or miss.
7. Holes: shell `INTERIOR` then hole `INTERIOR` ⇒ `EXTERIOR`; hole `BOUNDARY` ⇒ `BOUNDARY`.

PR claims: **4×+** faster than `IndexedPointInAreaLocator` on author benches; passes existing `AbstractPointInRingTest` suite.

---

## §2 — Corpus ground truth

| Asset | Role |
|---|---|
| [`docs/nts-oracle-gallery.md`](nts-oracle-gallery.md) | WKT + geometric answers + theorem names |
| `Overlay.point_in_ring` | Pure rightward-ray odd parity (strict y-straddle) |
| `JCT_VertexGrazingCounterexample.v` | Diamond centre: naive even, geometric **interior** |
| `JCT_HorizontalEdgeCounterexample.v` | Notch left exterior: naive odd, geometric **exterior** |
| `RectangleJCT.point_in_ring_rect_iff` | Half-open rect parity vs OGC boundary |
| `HotPixelConvexRing.v` | Pixel box + bottom-edge graze |
| `SpectreConcaveFamily.v` / `HatMonotile*.v` | Concave hull-interior exterior pockets |
| `WindingNumber.winding_decides_membership` | Parity ≡ winding under horizontal-edge guard |

**Convention note.** Gallery **GT** for production locators is the **geometric /
OGC Location** answer (`INTERIOR` / `EXTERIOR` / `BOUNDARY`), not bare parity.
Rows tagged `naive-parity≠geom` are exactly the robustness cases a correct
counter must not get wrong.

---

## §3 — Deliverables

| Path | Purpose |
|---|---|
| `tests/Discussion839Mre/YStripesPointInAreaLocator.cs` | Faithful C# port of PR tip (DD orient via NTS `CGAlgorithmsDD`) |
| `tests/Discussion839Mre/Program.cs` `--jts1145` | Gallery differential harness |
| `oracle/jts1145_pip_gallery_vectors.txt` | Machine table from one GREEN run |
| `tests/Discussion839Mre/jts-1145-comment.md` | Optional JTS PR comment draft |
| this doc | Lane write-up |

```text
dotnet run --project tests/Discussion839Mre -c Release -- --jts1145
# aliases: --ystripes --pip-gallery
```

---

## §4 — Results (2026-08-05, NTS 2.6)

| id | point | GT | naive parity | Indexed | Simple | YStripes | status |
|---|---|---|---|---|---|---|---|
| VGRAZE_G | (0, 0.5) | INT | odd | INT | INT | INT | PASS |
| **VGRAZE0** | **(0, 0)** | **INT** | **even\*** | INT | INT | INT | PASS |
| **HORZ_E** | **(−1, 1)** | **EXT** | **odd\*** | EXT | EXT | EXT | PASS |
| RECT_IN | (2, 1.5) | INT | in | INT | INT | INT | PASS |
| RECT_L | (0, 1.5) | BND | parity-in | BND | BND | BND | PASS |
| RECT_R | (4, 1.5) | BND | parity-out | BND | BND | BND | PASS |
| RECT_B | (2, 0) | BND | parity-out | BND | BND | BND | PASS |
| RECT_T | (2, 3) | BND | parity-out | BND | BND | BND | PASS |
| PIX_C | (0, 0) | INT | in | INT | INT | INT | PASS |
| PIX_BOT | (0, −0.5) | BND | parity-out\* | BND | BND | BND | PASS |
| SPECT_IN | (5, 0.5) | INT | odd | INT | INT | INT | PASS |
| **SPECT_POCK** | **(3.5, 0.5)** | **EXT** | even | EXT | EXT | EXT | PASS |
| HAT_IN | ≈(4.25, 2.165) | INT | odd | INT | INT | INT | PASS |
| **HAT_POCK** | ≈(3.5, 0.433) | **EXT** | even | EXT | EXT | EXT | PASS |

**14 / 14 PASS** · **0** YStripes≠GT · **0** Y≠Indexed · **0** Y≠Simple.

Critical robustness rows (bold) all GREEN:

| Vector | Why it matters | Result |
|---|---|---|
| Vertex graze diamond centre | Naive parity **false negative** (even) | YStripes **INT** |
| Horizontal edge left of notch | Naive parity **false positive** (odd) | YStripes **EXT** |
| Spectre reflex pocket | Concave / hull-interior exterior | YStripes **EXT** |
| Hat bottom pocket (APPROX √3 WKT) | Same class, irrational embedding | YStripes **EXT** |

Hat coordinates are gallery `APPROX` (exact theorems use √3); double WKT still
separates interior vs pocket under all three locators.

---

## §5 — Algorithm notes vs corpus

| Topic | YStripes (#1145) | Corpus `point_in_ring` |
|---|---|---|
| Y-straddle | half-open `(ay > py) != (by > py)` | strict `min < py < max` (similar effect) |
| Horizontal at query y | boundary if on segment; **no** parity flip | not a crossing (guard `no_horizontal_edge_at` for correctness) |
| Vertex graze | DD collinear → `ON` / directed orient | bare parity can miscount without `ray_avoids_vertices` |
| Output | OGC `Location` incl. `BOUNDARY` | pure bool interior parity |
| Index | Y-stripes (O(1) stripe + local segs) | none (specification predicate) |

Y-stripes is a **consumer** of the same geometric fact the gallery pins; it is
not a new mathematical predicate. No `Admitted` / claim registration required.

---

## §6 — Product / review recommendation

1. **Soundness (gallery)**: GREEN — merge is not blocked by vertex-graze /
horizontal-edge / concave-pocket classes covered here.
2. **Tests**: Existing `AbstractPointInRingTest` is necessary but thin on
concave monotiles and the diamond graze. Suggest adding the gallery WKT
pins (copy-paste from `nts-oracle-gallery.md` or this table) to
`YStripesPointInAreaLocatorTest` or the abstract suite so both Indexed and
YStripes stay locked.
3. **Port watch**: NTS should consider a twin after JTS merge (same package
layout under `Algorithm.Locate`).
4. **Limits of this lane**: C# port + NTS DD orient; not a Java bytecode run
of the PR branch. Behaviour is intentionally line-faithful; a Java re-run
of the same WKT in `YStripesPointInAreaLocatorTest` would close residual
port risk. Stress tests already on the PR cover random grids.

---

## §7 — Sources

- JTS PR tip: `micycle1/jts` branch `ystripes` (fetched 2026-08-05)
- Corpus gallery: `docs/nts-oracle-gallery.md`
- Scout ranking: `docs/jts-open-prs-scout-2026-08.md` item **#7** / work seq §3

**AI assistance**: Grok (grok-4.5), human-directed.
**License**: project documentation (BSD-3-Clause corpus).
14 changes: 10 additions & 4 deletions docs/jts-open-prs-scout-2026-08.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Ordered by **risk÷cost** for the proofs corpus (not JTS merge politics).
| **4** | [#1093](https://github.com/locationtech/jts/pull/1093) | Change Shewchuk Orientation filter to Ozaki et al. | **#66/#64** `precision`/`core` | H | M | H | Production `CGAlgorithmsDD` filter. **Lane GREEN 2026-08-05:** 45-vector three-way gate (master `1e-15` / Ozaki / corpus Shewchuk) vs `ORIENT_EXACT` — 0 CERTAIN conflicts; Ozaki tighter than master on 4 same-sign band cases. See [`jts-1093-orient-lane-2026-08.md`](jts-1093-orient-lane-2026-08.md). |
| **5** | [#1197](https://github.com/locationtech/jts/pull/1197) | Characterize DD orientation soundness (#1106) | **#66** `precision` | H | L | M | **Ours.** Test-only + `RocqRefRunner`. **Keep-green 2026-08-05 GREEN:** rebased onto master; corpus integer pins refreshed; local 22/22 + GHA `build-and-test` pass; merge ping posted ([comment](https://github.com/locationtech/jts/pull/1197#issuecomment-5186644725)). Awaiting maintainer review/merge. |
| **6** | [#90](https://github.com/locationtech/jts/pull/90) | Fix ScaledNoder behaviour (draft) | **#66** `precision` · **P1 policy** | H | L | M | **Lane YELLOW 2026-08-05 (not P0 theory):** mukoki scale=1 mixed-precision **masked** by modern `SnapRoundingNoder`; scale=0 `round(x*0)` footgun live; do **not** merge draft as written. No new snap claim. See [`jts-90-scalednoder-lane-2026-08.md`](jts-90-scalednoder-lane-2026-08.md). |
| **7** | [#1145](https://github.com/locationtech/jts/pull/1145) | YStripesPointInAreaLocator | **#67** `relate` (+ JCT) | H | L–M | M | New PIP implementation. Corpus `point_in_ring` + gallery (`nts-oracle-gallery.md`) is the **regression oracle** — run hat/Spectre/vertex-graze/horizontal-edge vectors. |
| **7** | [#1145](https://github.com/locationtech/jts/pull/1145) | YStripesPointInAreaLocator | **#67** `relate` (+ JCT) | H | L–M | M | **Lane GREEN 2026-08-05:** gallery 14/14 (vertex-graze/horizontal/Spectre/Hat) — YStripes port ≡ Indexed ≡ Simple ≡ geometric GT. See [`jts-1145-pip-lane-2026-08.md`](jts-1145-pip-lane-2026-08.md). |
| **8** | [#1084](https://github.com/locationtech/jts/pull/1084) | CoverageEdgeExtractor | **#425** `coverage` | M–H | M | M | Unique edges from polygonal coverage. Corpus has `CoverageGapOverlapCleaner.v` / #425. Good **next coverage claim** after cleaner. |
| **9** | [#1216](https://github.com/locationtech/jts/pull/1216) | Explicit stack in DouglasPeucker (#1127) | **#69** S-* | M | L | L–M | **Ours.** Stack overflow fix; behaviour-preserving. Corpus `Simplify.v` is structural only — **no new proof** needed; port to NTS when merged. |
| **10** | [#1140](https://github.com/locationtech/jts/pull/1140) | Hausdorff distance percentile | **#423** `metric` | M | M | L–M | Extends discrete Hausdorff. Corpus `HausdorffDiscrete.v` / Claim423a pins directed max-min — can pin percentile semantics or mark out-of-scope. |
Expand Down Expand Up @@ -81,7 +81,12 @@ Ordered by **risk÷cost** for the proofs corpus (not JTS merge politics).
`feature/orientation-robustness-tests` onto `master`; refreshed
`orientation_proof_vectors.txt` integer pins; local suite 22/22 + GHA
`build-and-test` pass; merge-ping comment posted.
3. **PIP gallery on #1145 (P0/P1)** — Reuse `docs/nts-oracle-gallery.md` WKT as Java tests / differential.
3. **PIP gallery on #1145 (P0/P1)** — **LANDED 2026-08-05 GREEN:**
- Faithful C# port: `tests/Discussion839Mre/YStripesPointInAreaLocator.cs`
- Harness: `dotnet run … -- --jts1145` (+ `oracle/jts1145_pip_gallery_vectors.txt`)
- Write-up: [`docs/jts-1145-pip-lane-2026-08.md`](jts-1145-pip-lane-2026-08.md).
- 14/14 geometric GT; critical rows (diamond graze INT, notch horizontal EXT, Spectre/Hat pockets EXT) all pass; Y ≡ Indexed ≡ Simple.
- Optional: post gallery pins comment (`tests/Discussion839Mre/jts-1145-comment.md`).
4. **ScaledNoder #90 (P1)** — **SCOPED 2026-08-05 YELLOW:**
- MRE: `tests/Discussion839Mre` `--jts90` + [`jts-90-scalednoder-lane-2026-08.md`](jts-90-scalednoder-lane-2026-08.md).
- Does **not** map to a missing `SnapRoundingScale_b64` claim (wrapper policy + scale=0 safety).
Expand Down Expand Up @@ -111,7 +116,7 @@ Columns: **Pri** = P0/P1/P2 · **Rel/Cost/Risk** · **Epic**.
| 1189 | WIP Orientation isCCW GEOS test | strk | P1 | H | L | M | precision | `status-INVALID`; still a useful negative test seed |
| 1170 | Generics on geom.util extractors | micycle1 | P2 | L | L | L | — | API typing |
| 1164 | GeoJsonWriter create() for subclasses | krizleebear | P2 | L | L | L | — | IO |
| **1145** | **YStripesPointInAreaLocator** | micycle1 | **P0** | H | L–M | M | **relate**/JCT | New PIP; gallery |
| **1145** | **YStripesPointInAreaLocator** | micycle1 | **P0** | H | L–M | M | **relate**/JCT | **GREEN** gallery 14/14 ([lane](jts-1145-pip-lane-2026-08.md)) |
| **1140** | **Hausdorff distance percentile** | ikgh9 | **P1** | M | M | L–M | **metric** | Extends HausdorffDiscrete |
| 1137 | Gh pages to docs | jodygarnett | P2 | L | L | L | — | docs |
| 1135 | HilbertEncoder.sort() | micycle1 | P2 | L | L | L | — | spatial index util |
Expand Down Expand Up @@ -190,6 +195,7 @@ Columns: **Pri** = P0/P1/P2 · **Rel/Cost/Risk** · **Epic**.
|---|---|---|
| **In-circle robustness** | #311 → #1094 → **#1212** | Treat **#1212 as tip**; archive others as predecessors once merged. History: [`jts-311-incircle-lane-2026-08.md`](jts-311-incircle-lane-2026-08.md). FFI scout + review: [`jts-1094-incircle-lane-2026-08.md`](jts-1094-incircle-lane-2026-08.md). Tip differential: [`jts-1212-incircle-lane-2026-08.md`](jts-1212-incircle-lane-2026-08.md) |
| **Orientation filters** | #1093 (Ozaki production), #1197 (DD limits tests), #1189 (isCCW test INVALID) | **#1093 lane GREEN** ([jts-1093-orient-lane-2026-08.md](jts-1093-orient-lane-2026-08.md)); keep #1197; mine #1189 for vectors only |
| **PIP / YStripes** | **#1145** | **GREEN** ([jts-1145-pip-lane-2026-08.md](jts-1145-pip-lane-2026-08.md)): gallery 14/14; free CI for locator merge |
| **ScaledNoder policy** | #90 (draft) | **YELLOW** ([jts-90-scalednoder-lane-2026-08.md](jts-90-scalednoder-lane-2026-08.md)): SRN masks mukoki; scale=0 only; no algebra claim |
| **DistanceOp / LineSegment** | #926, #930 | One review pass if either moves; low proof cost |
| **CI matrix** | #638, #646 | Ignore unless JTS maintainers revive |
Expand Down Expand Up @@ -245,7 +251,7 @@ Do **not** open multi-session theory work for P2 PRs.

1. **Bet the mesh lane on #1212** (with #1094/#311 as history) — highest corpus leverage per hour.
2. **Orientation filter change (#1093) gated on exact orient** — session GREEN; re-run table if the PR is revised.
3. **Use the PIP gallery as free CI for #1145**.
3. **Use the PIP gallery as free CI for #1145** — **done GREEN** (14/14); suggest pinning WKT into JTS tests.
4. **Ignore ~55% of the open queue** (CI/IO/docs/API) for proof planning.
5. **Keep our four PRs** green; only #1197 and #1216 need corpus-adjacent attention.

Expand Down
27 changes: 27 additions & 0 deletions oracle/jts1145_pip_gallery_vectors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# JTS #1145 YStripes PIP gallery differential (2026-08-05)
# Source: docs/nts-oracle-gallery.md + C# port of micycle1/jts ystripes tip
# Harness: tests/Discussion839Mre --jts1145
# claimId: none — ADR-0004 skip; harness-only differential, not a fresh mutation surface
# GT = geometric / OGC Location; parity column notes naive ray-parity where it differs
# status: all PASS (YStripes == Indexed == Simple == GT)
#
# id x y GT parity Indexed Simple YStripes status theorem

VGRAZE_G 0 0.5 INT odd INT INT INT PASS diamond_point_in_ring_A
VGRAZE0 0 0 INT even* INT INT INT PASS diamond_refutes_parity_seam
HORZ_E -1 1 EXT odd* EXT EXT EXT PASS notch_refutes_parity_without_guard
RECT_IN 2 1.5 INT in INT INT INT PASS point_in_ring_rect_iff
RECT_L 0 1.5 BND parity-in BND BND BND PASS point_in_ring_rect_iff_left
RECT_R 4 1.5 BND parity-out BND BND BND PASS point_in_ring_rect_iff_right
RECT_B 2 0 BND parity-out BND BND BND PASS point_in_ring_rect_iff_bottom
RECT_T 2 3 BND parity-out BND BND BND PASS point_in_ring_rect_iff_top
PIX_C 0 0 INT in INT INT INT PASS unit_pixel_centre_in_ring
PIX_BOT 0 -0.5 BND parity-out* BND BND BND PASS pixel_grazing_bottom_edge
SPECT_IN 5 0.5 INT odd INT INT INT PASS spectre_parity_classification
SPECT_POCK 3.5 0.5 EXT even EXT EXT EXT PASS spectre_parity_classification_pocket
HAT_IN 4.25 2.1650635094610964 INT odd INT INT INT PASS hat_parity_classification_interior_APPROX
HAT_POCK 3.5 0.4330127018922193 EXT even EXT EXT EXT PASS hat_parity_classification_pocket_APPROX

# Summary: 14/14 PASS, 0 YStripes≠GT, 0 Y≠Indexed, 0 Y≠Simple
# Critical robustness: VGRAZE0 INT, HORZ_E EXT, SPECT_POCK EXT, HAT_POCK EXT — all GREEN
# Write-up: docs/jts-1145-pip-lane-2026-08.md
Loading
Loading