Skip to content

Commit 36ed1dc

Browse files
Thin OrientableSegment adapter on ExactCircularArc (Bible §3) (#66)
* feat(curve): Sync program dump + M.3 D-HF-IWD exact Curve* hook Land CHAINSAW_LASER_PROGRAM.md and retip ratchet/seams/epic/METRIC_KIT. Hook DirectedHausdorffDistance.isFullyWithinDistance through exactOrientedPoints for certified arc/disc pairs; IWD + PERF-GATE tests. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * feat(curve): R.2 half-disc / open CircularString DE-9IM relate Recognise certified half-disc shells and single open arcs in CurveExact.relate. Point / line / same-circle disc matrices; diameter BI=1 run. Tests + seams sew. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * feat(curve): ML.4 ObstacleDistance hole-free CurvePolygon shell assembly Filled shell component: interior distance 0 via covers/PIP, exterior min over typed shell arcs/segments. Half-disc contains/covers exact; LEC tests. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * docs: retip Sync→M.3→R.2→ML.4 stop at c293fe9 Mark sequence complete on program/ratchet/epic; no further rungs. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * feat(curve): M.5 concentric-ring continuous Frechet + ML.2 half-disc MIC M.5: DiscreteFrechetDistance closed form for concentric CircularString rings (F=|R-r|). ML.2: CurveExact.halfDiscMic / mic() r=R/2 on axis. Tests + program sequence docs. Skip M.4 / R.3. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * feat(curve): HP.4 faces-after-snap stamp + N-SS Option-B crossing expand HP.4: CurveSegmentFaces wires CurveHotPixelSnap; SHARED_SNAPPED_RAY stamp on canonical N≥3 fixture (not a curvature-order walk). N-SS: OverlayNGCircle expands to proper-crossing two-shell assemble via TwoShellClip after Option-B noder (half×hanging). Not 74-file. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * docs: retip M.5→ML.2→HP.4→N-SS expand stop at c956b50 Mark sequence complete; holds M.4/R.3/ML.3/HP.5/74-file/D-HF TAG. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * feat(curve): B-team Proofs Option B OrientableSegment + 1M-trial handover Predicate seam: StraightOrientableSegment (core Orientation/RLI parity) and ArcOrientableSegment (tangent-frame side + circle intersect). Million-trial suites S1/S2/A1/A2/P1; handover doc + artifact JSON. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * refactor(curve): B-team OrientableSegment — ArcGeometry + DD side Maintainability: slim OrientableSegment API; ArcGeometry owns circle/sweep/ intersect; densify oracle test-only. Precision: CGAlgorithmsDD.signOfDet2x2 for arc tangent-frame side. PERF still ≪ densify (p50 ~0.42×). Re-ran 1M. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * refactor(curve): B round-2 — AngleBetween + cached arc + filter→DD Share AngleBetween with A (one sweep transcendental). Cache circumcircle on ArcOrientableSegment; filter-then-DD side (Orientation family); allocation-light hot path. 1M: S1/S2 1.0, A1 0.996151, A2 0.999996, P1 densify ratio 0.105 (beats A length-cell 0.173 on our workload). Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * feat(curve): lightweight OrientableSegment on ExactCircularArc (A) B predicates wrap A's ExactCircularArc; snapshot public getters for the side hot path; AngleBetween.travelled for sweep; ArcGeometry keeps only intersect/sample. Removed duplicate exactarc.AngleBetween. 1M green. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * refactor(curve): thinner OrientableSegment on ExactCircularArc ArcOrientableSegment delegates circle/sweep to A (centerX/Y, isOnSweep). Densify trials sample via ExactCircularArc.pointAt. ArcIntersects is the only densifier bridge. Removed ArcGeometry. 1M still green (P1 ~0.12). Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * docs(curve): prose align OrientableSegment with Exact Curve Bible Bible §3: ExactCircularArc privileged; OrientableSegment demoted to optional adapter that composes Exact*. Update package-info, javadoc, PROOFS_OPTION_B, CHAINSAW, handover preamble. Restore deprecated exactarc.AngleBetween alias per bible Year-1 notes. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * fix(curve): thin OrientableSegment for Bible / PR #62 review Public surface: OrientableSegment (start/end/length/side/intersects) + OrientableSegments factory only. Impls package-private and compose ExactCircularArc. AngleBetween package-private in exactcurve; removed public exactarc AngleBetween shim. Docs state adapter demotion. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> * fix(curve): OrientableSegment review nits — centre, filter, endpoints Centre query returns COLLINEAR (documented). Relative FILTER_EPS scale. endpointOnArc uses ulp(r) radialTol. ArcIntersects docs: Exact* owns future exact ∩. Tests for centre + shared-endpoint. Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jeroen Bloemscheer <grootstebozewolf@users.noreply.github.com>
1 parent 1e2b919 commit 36ed1dc

15 files changed

Lines changed: 1093 additions & 45 deletions

doc/CHAINSAW_LASER_PROGRAM.md

Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,32 @@
11
# Chainsaw → Laser · arc-native program
22

3-
Tip pin: session @ `8787709a` (A-team advance, parallel to B).
3+
Tip pin: `feature/sfa-curve-rgr` with ExactCurve* Year-1 lock (#63) and optional OrientableSegment adapter.
4+
Canonical architecture: [`EXACT_CURVE_BIBLE.md`](EXACT_CURVE_BIBLE.md).
45

5-
**A-team active:** Year-1 lock of `ExactCircularArc` per [`EXACT_CURVE_BIBLE.md`](EXACT_CURVE_BIBLE.md) (`exactcurve` package + thin `ExactCurve` protocol).
6+
**Year-1 lock:** `ExactCircularArc` is the privileged pure primitive (`exactcurve` + thin `ExactCurve`).
7+
`OrientableSegment` is a demoted, optional adapter (Bible §3).
68

7-
Prior: M.5→ML.2→HP.4→N-SS expand **stop** @ `c956b50d`.
9+
Prior sequence stop: M.5→ML.2→HP.4→N-SS expand @ `c956b50d`.
810

9-
Contract: parity or named densify-shim · `t_laser ≤ 1.15 × t_chainsaw` · no silent ConcaveHull · OverlayNGCurve never *Curved*.
10-
11-
Scoreboard (user board): **green 16 · chainsaw-only 3 · measured 11 hold**. See [LASER_RATCHET.md](LASER_RATCHET.md).
12-
13-
## Holds (still bind)
14-
15-
- Full **D-HF TAG** green (`fail()` kept)
16-
- **M.4** stadium HD · **R.3** stadium relate · **ML.3** MIC miss catalog · **HP.5** core HotPixel
17-
- 74-file N-SS Option B *lie* · curvature-order face walk on `SHARED_SNAPPED_RAY`
11+
Contract: parity or named densify-shim · `t_laser ≤ 1.15 × t_chainsaw` · no silent ConcaveHull · OverlayNGCurve never *Curved* · core `SegmentString` stays linear.
1812

1913
## Glossary
2014

2115
| Term | Meaning |
2216
|------|---------|
23-
| Proofs **A** | Exact arc closed-form cells (this team) |
24-
| Proofs **B** | Predicate abstraction OrientableSegment (sister team) |
17+
| **ExactCurve*** | Privileged immutable value types (`exactcurve`) |
18+
| **OrientableSegment** | Thin optional side/intersect adapter — not the centre of design |
2519
| JTS MMF “Option B” | `SegmentKind` typed carrier — orthogonal |
2620

27-
## A-team status
28-
29-
See [PROOFS_OPTION_A.md](PROOFS_OPTION_A.md) · handover [PROOFS_OPTION_A_HANDOVER.md](PROOFS_OPTION_A_HANDOVER.md).
30-
31-
Notion split: https://app.notion.com/p/3bf1c9833b0681738c39f5b8732b38e7
21+
## Holds
3222

33-
## This sequence (landed)
34-
35-
| Step | Rung | SHA / note |
36-
|------|------|------------|
37-
| M.5 | Continuous Fréchet concentric rings F=`|R−r|` | in `37e3dfe3` |
38-
| ML.2 | HALF_DISC MIC r=R/2 on axis | in `37e3dfe3` |
39-
| HP.4 | Faces after snap → `SHARED_SNAPPED_RAY` stamp | `c956b50d` |
40-
| N-SS | Option-B proper-crossing → TwoShellClip | `c956b50d` |
41-
| stop || this retip |
42-
43-
## Prior sequence
44-
45-
Sync → M.3 → R.2 → ML.4 @ `c293fe97` / docs `1cd9f498`.
46-
47-
## Kit map (abbreviated)
48-
49-
### Metric — M.5 **Landed** (M.4 hold)
50-
### MIC — ML.2 **Landed** (ML.3 keep grid)
51-
### HP — HP.4 **Landed** as honest stamp (not a walk laser)
52-
### N-SS — expanded one cell; full hierarchy still deferred
23+
- Full **D-HF TAG** green (`fail()` kept)
24+
- **M.4** / **R.3** / **ML.3** / **HP.5**
25+
- 74-file N-SS lie · curvature-order on `SHARED_SNAPPED_RAY`
5326

54-
## Related
27+
## Pointers
5528

56-
- [LASER_RATCHET.md](LASER_RATCHET.md)
57-
- [OVERLAYNGCURVE_P2_SEAMS.md](OVERLAYNGCURVE_P2_SEAMS.md)
58-
- [METRIC_KIT_MX.md](METRIC_KIT_MX.md)
59-
- [MMF_OPTION_B.md](MMF_OPTION_B.md)
60-
- [PROOFS_OPTION_A.md](PROOFS_OPTION_A.md)
29+
- [EXACT_CURVE_BIBLE.md](EXACT_CURVE_BIBLE.md)
30+
- [PROOFS_OPTION_A.md](PROOFS_OPTION_A.md) · [PROOFS_OPTION_A_HANDOVER.md](PROOFS_OPTION_A_HANDOVER.md)
31+
- [ORIENTABLE_SEGMENT_ADAPTER.md](ORIENTABLE_SEGMENT_ADAPTER.md) · [PROOFS_OPTION_B.md](PROOFS_OPTION_B.md)
32+
- [LASER_RATCHET.md](LASER_RATCHET.md) · [OVERLAYNGCURVE_P2_SEAMS.md](OVERLAYNGCURVE_P2_SEAMS.md)

doc/EXACT_CURVE_BIBLE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,7 @@ This document supersedes all previous Proofs option discussions, temporary STOP
157157
true (closed form). `isArc()` is the circular-vs-chord discriminator and
158158
is **not** on the thin protocol.
159159
- `toLinear(tolerance)` is the only densify path and is named as such.
160+
- Optional adapter: `OrientableSegment` / `OrientableSegments` (Bible §3) —
161+
public surface is start/end/length/orientationIndex/intersects only;
162+
implementations are package-private and compose `ExactCircularArc`.
163+
See `doc/ORIENTABLE_SEGMENT_ADAPTER.md`.

doc/ORIENTABLE_SEGMENT_ADAPTER.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# OrientableSegment — thin optional adapter
2+
3+
Canonical: [`EXACT_CURVE_BIBLE.md`](EXACT_CURVE_BIBLE.md) §3.
4+
5+
**`ExactCircularArc` is the privileged primitive.**
6+
This package is a demoted optional adapter for **side** and **intersect** only. It composes ExactCircularArc; it does not re-derive geometry.
7+
8+
## Public surface
9+
10+
| API | Role |
11+
|-----|------|
12+
| `OrientableSegment` | `getStart`, `getEnd`, `length`, `orientationIndex`, `intersects` |
13+
| `OrientableSegments` | `straight`, `arc(ExactCircularArc)`, `arc(s,m,e)` |
14+
15+
Implementations are package-private.
16+
17+
```java
18+
ExactCircularArc exact = new ExactCircularArc(s, m, e);
19+
OrientableSegment seg = OrientableSegments.arc(exact);
20+
```

doc/PROOFS_OPTION_B.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# OrientableSegment — thin optional adapter (Bible §3)
2+
3+
Canonical: [`EXACT_CURVE_BIBLE.md`](EXACT_CURVE_BIBLE.md).
4+
5+
## Architectural role
6+
7+
**`ExactCircularArc` is the privileged pure primitive.**
8+
`OrientableSegment` is a **demoted, optional adapter**. It must not become the centre of the design. It **composes** `ExactCircularArc`; it does not re-derive circumcircle or sweep.
9+
10+
Addresses PR #62 review: thin public surface, no public `AngleBetween` shim, forced composition, Exact* in `exactcurve`.
11+
12+
## Public surface (minimal)
13+
14+
| Type | Visibility | Members |
15+
|------|------------|---------|
16+
| `OrientableSegment` | public | `getStart`, `getEnd`, `length`, `orientationIndex`, `intersects` |
17+
| `OrientableSegments` | public | `straight`, `arc(ExactCircularArc)`, `arc(s,m,e)` |
18+
| `ArcOrientableSegment` / `StraightOrientableSegment` | **package-private** ||
19+
| `exactcurve.AngleBetween` | **package-private** | sweep helper for ExactCircularArc only |
20+
| `exactarc.*` | **removed** | no public shim |
21+
22+
## Factory
23+
24+
```java
25+
ExactCircularArc exact = new ExactCircularArc(s, m, e);
26+
OrientableSegment seg = OrientableSegments.arc(exact);
27+
```
28+
29+
## Trials
30+
31+
[`PROOFS_OPTION_B_HANDOVER.md`](PROOFS_OPTION_B_HANDOVER.md)

doc/PROOFS_OPTION_B_HANDOVER.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# OrientableSegment adapter — 1M-trial handover
2+
3+
Bible §3: ExactCircularArc privileged; these trials cover the optional OrientableSegment side/intersect adapters only.
4+
5+
Seed `0xc0ffeeb007` · N=1000000 · box [-100.0,100.0]² · densify nChord=64 (via ExactCircularArc.pointAt)
6+
7+
### S1 straight orientationIndex vs Orientation.index
8+
9+
| metric | value |
10+
|---|---:|
11+
| tried | 1000000 |
12+
| hard disagree | 0 |
13+
| soft agree (on-curve / collinear tie) | 0 |
14+
| agree rate (1 - hard/tried) | 1.0 |
15+
| wall ns | 170300766 |
16+
17+
### S2 straight intersects vs RobustLineIntersector
18+
19+
| metric | value |
20+
|---|---:|
21+
| tried | 1000000 |
22+
| hard disagree | 0 |
23+
| soft agree (on-curve / collinear tie) | 0 |
24+
| agree rate (1 - hard/tried) | 1.0 |
25+
| wall ns | 358822845 |
26+
27+
### A1 arc orientationIndex vs densify reference
28+
29+
| metric | value |
30+
|---|---:|
31+
| tried | 1000000 |
32+
| hard disagree | 3849 |
33+
| soft agree (on-curve / collinear tie) | 0 |
34+
| agree rate (1 - hard/tried) | 0.996151 |
35+
| wall ns | 4316500697 |
36+
37+
### A2 arc×segment intersects vs densify+RLI
38+
39+
| metric | value |
40+
|---|---:|
41+
| tried | 1000000 |
42+
| hard disagree | 4 |
43+
| soft agree (on-curve / collinear tie) | 0 |
44+
| agree rate (1 - hard/tried) | 0.999996 |
45+
| wall ns | 4315755173 |
46+
47+
### P1 arc orientationIndex latency vs densify
48+
49+
| metric | value |
50+
|---|---:|
51+
| B p50 ns (50k calls) | 16581896 |
52+
| densify p50 ns | 141057842 |
53+
| ratio B/ref | 0.11755387552292201 |
54+
55+
56+
## Verdict
57+
58+
Optional OrientableSegment adapters: straight parity 100%; arc vs densify ≥ 0.99 (A1=0.996151, A2=0.999996); PERF p50 ratio 0.11755387552292201 ≤ 1.15.
59+
60+
Residual A1 hard disagrees are densify-chord vs arc-tangent frame disagreements off the curve (nChord=64), not silent flatten. ExactCircularArc remains the privileged primitive (Bible §3).

modules/curve/src/main/java/org/locationtech/jts/algorithm/exactcurve/ExactCircularArc.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ public double radius() {
105105
return r;
106106
}
107107

108+
/** Centre x; {@link Double#NaN} on a chord fallback. */
109+
public double centerX() {
110+
return cx;
111+
}
112+
113+
/** Centre y; {@link Double#NaN} on a chord fallback. */
114+
public double centerY() {
115+
return cy;
116+
}
117+
108118
public Coordinate center() {
109119
return arc ? new Coordinate(cx, cy) : null;
110120
}
@@ -114,6 +124,24 @@ public double sweep() {
114124
return sweep;
115125
}
116126

127+
/**
128+
* Whether {@code p}'s central angle lies on this directed window.
129+
* Chord fallback is always {@code false}.
130+
*/
131+
public boolean isOnSweep(Coordinate p) {
132+
return onSweep(p);
133+
}
134+
135+
/** Allocation-free sweep test at Cartesian {@code (x, y)}. */
136+
public boolean isOnSweep(double x, double y) {
137+
if (!arc) {
138+
return false;
139+
}
140+
double travelled = AngleBetween.travelled(ccw,
141+
start.x - cx, start.y - cy, x - cx, y - cy);
142+
return travelled <= sweep + Math.ulp(sweep);
143+
}
144+
117145
public double length() {
118146
if (!arc) {
119147
return start.distance(end);

0 commit comments

Comments
 (0)