BREP_kernel 0.13.1

A boundary representation (BREP) geometry kernel for building CAD applications.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
use crate::{KernelRefusal, KernelStage, OrRefuse};
use super::*;

/// General OPEN-edge rolling-ball fillet or chamfer: §4.9 march +
/// §6.9 surgery with transverse edges on the two end faces.
pub fn blend_open_edge(
    solid: &BrepSolid,
    edge_id: u64,
    radius: f64,
    chamfer: bool,
    name: Option<&str>,
) -> Result<BrepSolid, KernelRefusal> {
    // One blend operation: the reports of an earlier one, never consumed, are
    // dropped as it starts (nested calls keep this operation's).
    let _operation = crate::blend::BlendOperation::enter();
    if !(radius > 0.0) || !radius.is_finite() {
        return Err(KernelRefusal::input(KernelStage::Collect, "radius", "blend: radius must be positive"));
    }
    blend_open_edge_impl(solid, edge_id, &|_| radius, Some(radius), chamfer, name)
}

/// `constant`: the radius when the blend came in through a CONSTANT-radius
/// entry point (`blend_open_edge`), `None` for a variable profile — the only
/// provenance on which the wall is judged against one rolling ball.
pub(super) fn blend_open_edge_impl(
    solid: &BrepSolid,
    edge_id: u64,
    radius_at: &dyn Fn(f64) -> f64,
    constant: Option<f64>,
    chamfer: bool,
    name: Option<&str>,
) -> Result<BrepSolid, KernelRefusal> {
    let edge = solid
        .edges
        .iter()
        .find(|edge| edge.id == edge_id)
        .ok_or_else(|| format!("blend: edge {edge_id} not found")).or_refuse(KernelStage::Refine, "ok_or_else")?;
    if edge.start_vertex_id == edge.end_vertex_id {
        return Err(KernelRefusal::input(KernelStage::Collect, "open_edge", "blend: blend_open_edge requires an open edge"));
    }
    let (first_face, first_loop, first_coedge) = locate_mate(solid, edge_id, None)?;
    let (second_face, second_loop, second_coedge) =
        locate_mate(solid, edge_id, Some((first_face.id, first_loop)))?;
    let mid_radius = radius_at(edge.t0 + (edge.t1 - edge.t0) * 0.5);
    let (rho1, rho2) = signed_radii(
        edge,
        first_face,
        first_coedge,
        second_face,
        second_coedge,
        mid_radius,
    )?;
    let first_mate = BlendMate {
        face: first_face,
        coedge: first_coedge,
        loop_index: first_loop,
        rho: rho1,
    };
    let second_mate = BlendMate {
        face: second_face,
        coedge: second_coedge,
        loop_index: second_loop,
        rho: rho2,
    };
    // End topology: boundary edges of both mates at each end vertex, the
    // single face across the corner, and the support crossings.  When a
    // prior fillet has already consumed one of the end corners, the support
    // crossing can land at the very rim of the marched rows (the prior
    // blend's transverse arc meets our contact line right at its base).
    // March with a growing overshoot until every crossing lands strictly
    // inside the fitted-row domain so the surgery can trim cleanly.
    let debug = std::env::var("BREP_DEBUG_BLEND_MARCH").is_ok();
    if debug {
        let sp = edge.curve.evaluate(edge.t0);
        let ep = edge.curve.evaluate(edge.t1);
        eprintln!(
            "OPEN edge {} v{}->v{} first_face {} second_face {} p0={:?} p1={:?}",
            edge.id,
            edge.start_vertex_id,
            edge.end_vertex_id,
            first_face.id,
            second_face.id,
            sp,
            ep
        );
    }
    // The rung's MEASUREMENT (before anything is built on it), over what it
    // ships: each rail against its carrier's extension over its own crossing
    // window (`rails_on_shipped_windows`, at the network lane's rail bar, half
    // `intersection_fit`), and a constant-radius FILLET's wall over those
    // windows' envelope against the rolling ball's own sweep (the declared
    // reader the chains use, at `intersection_fit`).
    let rail_bar = 0.5 * crate::KernelTolerances::for_solid(solid, 1e-7).intersection_fit;
    let wall_bar = crate::KernelTolerances::for_solid(solid, 1e-7).intersection_fit;
    let model = crate::KernelTolerances::for_solid(solid, 1e-7).model;
    // Constant-radius PROVENANCE (the entry point), never sampled constancy:
    // a variable profile can agree at any finite set of samples.
    let wall_radius = constant.filter(|radius| radius.is_finite() && *radius > 0.0);
    let wall_check = !chamfer && wall_radius.is_some() && crate::blend::station_refinement_on();
    let compute = |overshoot_fraction: f64, station_count: usize| -> Result<(FittedRows, Vec<EndSurgery>, bool, OpenRungReading), KernelRefusal> {
        // The open-edge surgery locates its ends by support crossings, not by
        // the marched vertex stations; the snapped indices only break the fit
        // where the carriers' extension starts (`fit_open_rows`).
        let (stations, vertex_indices, refinement, dense) = march_open_stations_core(
            edge,
            &first_mate,
            &second_mate,
            radius_at,
            overshoot_fraction,
            [edge.t0, edge.t1],
            station_count,
            [false, false],
            [false, false],
            crate::blend::station_refinement_on().then_some(rail_bar),
            if wall_check { OPEN_WALL_CENTRES } else { 0 },
            true,
        )?;
        let parameters = station_parameters(&stations);
        let rows = fit_open_rows(
            &stations,
            &parameters,
            chamfer,
            Some(vertex_indices),
            extrusion_direction(edge, &first_mate, &second_mate),
        )?;
        // The fitted wall itself must not double back along a row
        // (`fold::check_rows_loop`); terminal, as the march's fold verdicts.
        crate::blend::fold::check_rows_loop(
            &rows, &stations, &parameters, Some(vertex_indices), radius_at(0.5 * (edge.t0 + edge.t1)), vec![first_mate.face.id, second_mate.face.id], crate::solid_scale(solid),
        )?;
        let mut ends = Vec::with_capacity(2);
        let mut in_range = true;
        for (vertex, at_start) in [(edge.start_vertex_id, true), (edge.end_vertex_id, false)] {
            let (end, side_in_range) = resolve_free_end(
                solid,
                edge_id,
                &first_mate,
                &second_mate,
                &rows,
                vertex,
                at_start,
            )?;
            if debug {
                eprintln!(
                    "  [os {overshoot_fraction:.2}] end v{vertex} at_start={at_start} end_face={} first_boundary={}(t={:.4}) second_boundary={}(t={:.4}) cr={:.4} cs={:.4} in_range={side_in_range}",
                    end.end_face_id,
                    end.first_edge_id,
                    end.first_edge_parameter,
                    end.second_edge_id,
                    end.second_edge_parameter,
                    end.cr_parameter,
                    end.cs_parameter,
                );
            }
            in_range &= side_in_range;
            ends.push(end);
        }
        // The measurement covers what SHIPS: each rail over its own crossing
        // window (the `cr`/`cs` parameters `build_open_surgery` trims it to,
        // any retained overshoot included), and the wall over the envelope of
        // both windows -- not merely between the vertex stations.
        let windows = [
            shipped_window(&parameters, ends[0].cr_parameter, ends[1].cr_parameter),
            shipped_window(&parameters, ends[0].cs_parameter, ends[1].cs_parameter),
        ];
        let (rails, rail_samples) =
            rails_on_shipped_windows(&rows, &parameters, &stations, [&first_mate.face.surface, &second_mate.face.surface], windows);
        let walls = match (wall_check, windows) {
            (false, _) => Vec::new(),
            (true, [Some(first), Some(second)]) => open_wall_readings_why(
                &rows,
                &parameters,
                &stations,
                [first[0].min(second[0]), first[1].max(second[1])],
                &dense,
                wall_radius.unwrap_or(f64::NAN),
            ),
            // No shipped window to read: no wall coverage, never acceptance.
            (true, _) => vec![Err("no shipped window to read".to_string())],
        };
        let first_unread = walls.iter().find_map(|wall| wall.as_ref().err().cloned());
        let walls: Vec<Option<f64>> = if wall_check && windows.iter().any(|window| window.is_none()) {
            Vec::new()
        } else {
            walls.into_iter().map(|wall| wall.ok()).collect()
        };
        let reading = OpenRungReading {
            station_count,
            stations: stations.len(),
            rails,
            rail_samples,
            walls,
            first_unread,
            refine_exit: refinement.exit,
            inserted: refinement.inserted,
        };
        Ok((rows, ends, in_range, reading))
    };

    // Radius-aware seeded first attempt (occt-filleting-system-study §6(b)
    // lesson 7): OCCT floors corner extensions at 1.5·max_radius
    // (`ExtentTwoCorner`) instead of probing blindly.  Convert that floor to
    // an overshoot fraction of THIS edge: the contact rails sit r·tan(α/2)
    // from the edge (α = sign-adjusted angle between the mates' raw normals
    // at mid-edge — the march's own `cos_alpha`), so seed with 1.5× the
    // widest end radius's rail offset over the edge arc length, clamped to
    // the ladder's proven [0.08, 0.45] envelope.  Deterministic; `None` when
    // it degenerates or merely reproduces the ladder's first rung.
    let seeded_fraction: Option<f64> = (|| {
        let span = edge.t1 - edge.t0;
        let mut length = 0.0f64;
        let mut previous = edge.curve.evaluate(edge.t0).ok()?;
        for index in 1..=16 {
            let t = edge.t0 + span * index as f64 / 16.0;
            let point = edge.curve.evaluate(t).ok()?;
            length += point.sub(previous).length();
            previous = point;
        }
        if !(length > 0.0) || !length.is_finite() {
            return None;
        }
        let mid_t = edge.t0 + span * 0.5;
        let uv1 = edge_uv_on_face(first_mate.coedge, edge, mid_t).ok()?;
        let uv2 = edge_uv_on_face(second_mate.coedge, edge, mid_t).ok()?;
        let n1 = raw_normal(&first_mate.face.surface, uv1[0], uv1[1]).ok()?;
        let n2 = raw_normal(&second_mate.face.surface, uv2[0], uv2[1]).ok()?;
        let cos_alpha =
            (rho1.signum() * rho2.signum() * n1.dot(n2)).clamp(-1.0, 1.0);
        // tan(α/2) = √((1−cosα)/(1+cosα)); the tangent-offset factor from
        // the edge to each rail (box: α = π/2 → offset = r).
        let tan_half = ((1.0 - cos_alpha).max(0.0) / (1.0 + cos_alpha).max(1e-9)).sqrt();
        let end_radius = radius_at(edge.t0).abs().max(radius_at(edge.t1).abs());
        let fraction = (1.5 * end_radius * tan_half / length).clamp(0.08, 0.45);
        if !fraction.is_finite() || (fraction - 0.08).abs() < 1e-12 {
            return None;
        }
        Some(fraction)
    })();

    // Retry with a growing overshoot; keep the first attempt whose crossings
    // are all in range, else fall back to the widest march tried.  The seeded
    // attempt is accepted ONLY when its crossings land in range — otherwise
    // the blind ladder below runs exactly as before (including its
    // fallback-to-first-Ok semantics), so the seed can improve the first
    // landing but never change the fallback behaviour.
    let choose = |station_count: usize| -> Result<(FittedRows, Vec<EndSurgery>, OpenRungReading), KernelRefusal> {
    let mut chosen: Option<(FittedRows, Vec<EndSurgery>, OpenRungReading)> = None;
    let mut last_error: Option<KernelRefusal> = None;
    // A wall that FOLDS is the shape's answer, not this rung's: every wider
    // overshoot marches the same centre curve through the same bend, so the
    // ladder is left at the first fold rather than re-proving it four times.
    if let Some(fraction) = seeded_fraction {
        match compute(fraction, station_count) {
            Ok((rows, ends, true, reading)) => chosen = Some((rows, ends, reading)),
            Ok(_) => {}
            Err(error) if crate::blend::is_wall_fold(&error) => return Err(error),
            Err(error) => last_error = Some(error),
        }
    }
    if chosen.is_none() {
        for &overshoot_fraction in &[0.08f64, 0.16, 0.28, 0.45] {
            match compute(overshoot_fraction, station_count) {
                Ok((rows, ends, in_range, reading)) => {
                    let fallback = chosen.is_none();
                    if in_range {
                        chosen = Some((rows, ends, reading));
                        break;
                    } else if fallback {
                        chosen = Some((rows, ends, reading));
                    }
                }
                Err(error) if crate::blend::is_wall_fold(&error) => return Err(error),
                Err(error) => last_error = Some(error),
            }
        }
    }
    chosen.ok_or_else(|| {
        last_error.unwrap_or_else(|| {
            KernelRefusal::internal(
                KernelStage::Refine,
                "open_march",
                "blend: open march failed at every overshoot",
            )
        })
    })
    };
    // The LADDER: the open march's existing budget — STATIONS doubling to
    // MAX_OPEN_STATIONS, each rung with its local refinement (REFINE_ROUNDS,
    // REFINE_STATION_FACTOR) against the rail bar — climbed until a rung is
    // ACCEPTED (rails and wall inside their bars), then, for a plain fillet,
    // further toward `model` on the wall. A rung the request climbs to that
    // fails acceptance is not taken: the accepted rung ships and says so,
    // typed. A ladder that never accepts ships its top rung with a named note,
    // as the network lane's open march does (a body that built before is not
    // turned into a refusal here) -- but it is NOT an accepted rung: it ships
    // with a typed `blend.wall_acceptance` report of its measured deficit.
    // Acceptance is stated positively: every reading present, finite and
    // inside its bar (a NaN or an empty coverage never passes).
    let accepts = |reading: &OpenRungReading| {
        reading.rails_read()
            && reading.rails <= rail_bar
            && (!wall_check || reading.worst_wall().is_some_and(|wall| wall <= wall_bar))
    };
    let meets_model = |reading: &OpenRungReading| reading.worst_wall().is_some_and(|wall| wall <= model);
    let mut station_count = STATIONS;
    let mut accepted: Option<(FittedRows, Vec<EndSurgery>, OpenRungReading)> = None;
    // The top rung of a ladder that NEVER accepted: shipped as built, kept
    // apart from `accepted` so it is never reported as an accepted parent.
    let mut unaccepted: Option<(FittedRows, Vec<EndSurgery>, OpenRungReading)> = None;
    let mut request_rungs = 0usize;
    let mut unmet: Option<crate::BudgetReason> = None;
    loop {
        // A rung climbed to after acceptance is a request rung ATTEMPTED,
        // whatever it returns.
        if accepted.is_some() {
            request_rungs += 1;
        }
        let chosen = choose(station_count);
        let (rows, ends, reading) = match chosen {
            Ok(chosen) => chosen,
            // A request rung that cannot be built: the accepted rung stands.
            Err(_) if accepted.is_some() => {
                unmet = Some(crate::BudgetReason::Incoherent);
                break;
            }
            Err(error) => return Err(error),
        };
        #[cfg(not(test))]
        let reject_request = false;
        crate::blend::carve::carve_trace(format_args!(
            "blend open edge {}: {} stations a rung ({} in all), rails {:.3e} (bar {rail_bar:.3e}), wall {:.3e} (bar {wall_bar:.3e}){}",
            edge.id, reading.station_count, reading.stations, reading.rails,
            reading.worst_wall().unwrap_or(f64::INFINITY),
            if accepted.is_some() { ", a request rung" } else { "" }
        ));
        let accepted_now = accepts(&reading) && !reject_request;
        if accepted.is_none() {
            if accepted_now {
                let met = !wall_check || meets_model(&reading);
                accepted = Some((rows, ends, reading));
                if met {
                    break;
                }
            } else if station_count < MAX_OPEN_STATIONS {
                station_count *= 2;
                continue;
            } else {
                crate::blend::record_blend_note(format!(
                    "blend: open edge {} at {} stations a rung ({} in all) — the top of the open march's \
                     ladder — still leaves its rails {:.3e} from their carriers (bar {rail_bar:.3e}) or its \
                     wall off the rolling ball (bar {wall_bar:.3e}); the wall ships as built, unaccepted",
                    edge.id, reading.station_count, reading.stations, reading.rails
                ));
                unaccepted = Some((rows, ends, reading));
                break;
            }
        } else {
            if !accepted_now {
                unmet = Some(crate::BudgetReason::Rejected);
                break;
            }
            let met = meets_model(&reading);
            accepted = Some((rows, ends, reading));
            if met {
                unmet = None;
                break;
            }
        }
        // Accepted, short of `model`: one more rung if the budget allows.
        if station_count < MAX_OPEN_STATIONS {
            station_count *= 2;
        } else {
            unmet = Some(crate::BudgetReason::StationCeiling);
            break;
        }
    }
    let (shipped_accepted, (rows, ends, reading)) = match (accepted, unaccepted) {
        (Some(rung), _) => (true, rung),
        (None, Some(rung)) => (false, rung),
        (None, None) => {
            return Err(KernelRefusal::internal(KernelStage::Refine, "open_ladder", "blend: the open ladder ended without a rung"));
        }
    };
    // What the shipped wall owes, typed: an UNACCEPTED rung its measured
    // acceptance deficit (or that a reading could not be taken); an accepted
    // fillet short of `model` its construction request.
    let report: Option<(crate::BudgetReason, f64, f64, String)> = if !shipped_accepted {
        Some(acceptance_deficit(&reading, rail_bar, wall_bar, wall_check, reading.rails_read(), "no rung of the open ladder was accepted"))
    } else if wall_check && !meets_model(&reading) {
        let reason = unmet.unwrap_or(crate::BudgetReason::StationCeiling);
        // An accepted rung's every wall interval was read (acceptance needs it).
        let declared = reading.worst_wall().unwrap_or(f64::INFINITY);
        Some((reason, model, declared, String::new()))
    } else {
        None
    };

    let [start_end, finish_end] = match <[EndSurgery; 2]>::try_from(ends) {
        Ok(pair) => pair,
        Err(_) => return Err(KernelRefusal::internal(KernelStage::Sew, "open_surgery_ends", "blend: open surgery needs exactly two ends")),
    };
    // A crossing that only resolved on a looser rung of the tolerance ladder
    // builds something master refused outright, so it must earn its answer
    // (see [`SpokeCrossings`]).
    let mut crossings = SpokeCrossings::default();
    crossings.note(start_end.escalated || finish_end.escalated);
    let mut result = solid.clone();
    let mut take_id = fresh_id_source(solid);
    let sewn = build_open_surgery(
        solid,
        &mut result,
        &mut take_id,
        edge,
        &first_mate,
        &second_mate,
        rows,
        [EndPlan::Free(start_end), EndPlan::Free(finish_end)],
        name,
    )?;
    prune_orphan_vertices(&mut result);
    // Extended end boundaries can leave a finite planar chart. Rebuild those
    // charts before validating the surgery, rather than rejecting a correct
    // edge because its projected pcurve was clamped to the old rectangle.
    crate::blend::fit_planar_charts_to_trims(solid, &mut result)?;
    check_snap_closure(solid, &result, sewn.snap)?;
    let built = crossings.gate(result)?;
    // The wall AS SEWN: the surgery may transpose the face (an exact
    // extrusion), and a report is matched to its face by exact surface.
    let sewn_surface = built
        .shells
        .iter()
        .flat_map(|shell| &shell.faces)
        .find(|face| face.id == sewn.blend_face_id)
        .map(|face| face.surface.clone())
        .ok_or(KernelRefusal::internal(KernelStage::Sew, "open_blend_face", "blend: the sewn open blend face is not in the built body"))?;
    // A wall that SHIPS unaccepted, or short of its construction request,
    // says so, typed, on the built body's wall (exact name and sewn surface).
    if let Some((reason, bar, measured, deficit)) = report {
        // The budget is the open march's GLOBAL station ladder (STATIONS
        // doubling to MAX_OPEN_STATIONS), not local rounds: for a request,
        // the rungs ATTEMPTED past the accepted one (a refused one included);
        // for an unaccepted rung, the rungs climbed past the first. Each rung
        // also ran its own local rail refinement, named in the detail.
        let rungs_used = if shipped_accepted { request_rungs } else { (reading.station_count / STATIONS).trailing_zeros() as usize };
        let mut detail = format!(
            "the shipped rung's local rail refinement ended {:?} with {} stations inserted",
            reading.refine_exit, reading.inserted
        );
        if !deficit.is_empty() {
            detail = format!("{deficit}; {detail}");
        }
        crate::blend::record_wall_model_report(crate::blend::WallModelReport {
            name: name.map(str::to_string),
            surface: sewn_surface,
            request: bar,
            residual: measured,
            detail: Some(detail),
            budget: crate::ApproximationBudget {
                reason,
                rounds_used: rungs_used,
                rounds_limit: OPEN_REQUEST_RUNGS,
                stations: reading.stations,
                station_limit: REFINE_STATION_FACTOR * (MAX_OPEN_STATIONS + 1),
                mechanism: crate::BudgetMechanism::StationRungs,
                measured_component: crate::MeasuredComponent::Wall,
                unread: 0,
            },
        });
    }
    Ok(built)
}

/// An UNACCEPTED shipped rung's deficit, typed: `Unread` when any reading
/// acceptance needs could not be taken (a non-finite rail, an empty shipped
/// window, an unread or missing wall interval), else `Unaccepted`; the
/// measured value and its bar are the readable component worst against its
/// bar (rails against `rail_bar`, the wall against `wall_bar`; 0 against
/// `rail_bar` when nothing could be read), always finite. The text names both
/// readings against their bars, after `what` (what failed acceptance: the
/// open ladder's every rung, or a network stripe's shipped windows).
/// `rails_read` is the caller's verdict on whether the rails were READ (the
/// open lane: [`OpenRungReading::rails_read`]; a network stripe also counts a
/// validated collapsed rail as read with no samples).
pub(in crate::blend) fn acceptance_deficit(
    reading: &OpenRungReading,
    rail_bar: f64,
    wall_bar: f64,
    wall_check: bool,
    rails_read: bool,
    what: &str,
) -> (crate::BudgetReason, f64, f64, String) {
    let rails = (rails_read && reading.rails.is_finite() && reading.rails >= 0.0).then_some(reading.rails);
    let wall = if wall_check { reading.worst_wall() } else { None };
    let unread = rails.is_none() || (wall_check && wall.is_none());
    let mut worst = (0.0_f64, rail_bar);
    for (value, bar) in [(rails, rail_bar), (wall, wall_bar)] {
        if let Some(value) = value {
            if value / bar > worst.0 / worst.1 {
                worst = (value, bar);
            }
        }
    }
    let rail_text = match rails {
        Some(value) => format!("rails {value:.3e} from their carriers (bar {rail_bar:.3e})"),
        None => format!(
            "rails UNREAD ({:?} over {} + {} samples) against bar {rail_bar:.3e}",
            reading.rails, reading.rail_samples[0], reading.rail_samples[1]
        ),
    };
    let wall_text = match (wall_check, wall) {
        (false, _) => "the wall not judged (no constant-radius fillet provenance)".to_string(),
        (true, Some(value)) => format!("wall {value:.3e} off the rolling ball (bar {wall_bar:.3e})"),
        (true, None) => format!(
            "wall UNREAD on {} of {} intervals against bar {wall_bar:.3e}{}",
            reading.walls.iter().filter(|wall| !matches!(wall, Some(value) if value.is_finite())).count(),
            reading.walls.len(),
            reading.first_unread.as_deref().map(|why| format!(" (first: {why})")).unwrap_or_default()
        ),
    };
    let reason = if unread { crate::BudgetReason::Unread } else { crate::BudgetReason::Unaccepted };
    (reason, worst.1, worst.0, format!("{what}: {rail_text}, {wall_text}"))
}

/// Section probes per open wall interval (k/8 of the section), and centres
/// per interval (probes at the even ones), as the chains read their walls.
const OPEN_WALL_SECTIONS: usize = 8;
pub(in crate::blend) const OPEN_WALL_CENTRES: usize = 32;
/// The rungs the construction request may climb past the first accepted one:
/// the open march's own ladder, STATIONS doubling to MAX_OPEN_STATIONS.
pub(in crate::blend) const OPEN_REQUEST_RUNGS: usize = (MAX_OPEN_STATIONS / STATIONS).trailing_zeros() as usize;

/// One open rung's measurement over what it SHIPS: its rung, its station
/// count (refinement included), the rails' worst distance from their
/// carriers over each rail's own crossing window and the samples read on
/// each (0: no window), each wall interval's declared reading (`None`:
/// unreadable) over the windows' envelope, and how the rung's own LOCAL rail
/// refinement ended (its exit and the stations it inserted).
#[derive(Clone, Debug)]
pub(in crate::blend) struct OpenRungReading {
    pub(in crate::blend) station_count: usize,
    pub(in crate::blend) stations: usize,
    pub(in crate::blend) rails: f64,
    pub(in crate::blend) rail_samples: [usize; 2],
    pub(in crate::blend) walls: Vec<Option<f64>>,
    /// The first unread wall interval and why (diagnostic; `None` when every
    /// interval read or the wall was not judged).
    pub(in crate::blend) first_unread: Option<String>,
    pub(in crate::blend) refine_exit: crate::blend::stations::StationRefineExit,
    pub(in crate::blend) inserted: usize,
}

impl OpenRungReading {
    /// Every rail READ: a finite, nonnegative worst distance from samples on
    /// both rails' shipped windows. A NaN, an infinity (an unreadable foot)
    /// or an empty window is never a reading.
    pub(in crate::blend) fn rails_read(&self) -> bool {
        self.rails.is_finite() && self.rails >= 0.0 && self.rail_samples.iter().all(|&samples| samples > 0)
    }

    /// Every wall interval READ (and at least one): `None` when any is not.
    pub(in crate::blend) fn worst_wall(&self) -> Option<f64> {
        if self.walls.is_empty() {
            return None;
        }
        self.walls.iter().try_fold(0.0_f64, |worst, wall| match wall {
            Some(declared) if declared.is_finite() && *declared >= 0.0 => Some(worst.max(*declared)),
            _ => None,
        })
    }
}

/// The shipped window `[low, high]` of one rail between its two crossing
/// parameters, within the rows' parameters (the surgery refuses a crossing
/// outside them; the reading only never indexes past them). `None` when the
/// window is empty or not finite: nothing ships there that could be read.
pub(in crate::blend) fn shipped_window(parameters: &[f64], a: f64, b: f64) -> Option<[f64; 2]> {
    let (&first, &last) = (parameters.first()?, parameters.last()?);
    let (low, high) = (a.min(b).max(first), a.max(b).min(last));
    (low.is_finite() && high.is_finite() && low < high).then_some([low, high])
}

/// The station interval holding `u` and `u`'s fraction of it.
fn interval_at(parameters: &[f64], u: f64) -> Option<(usize, f64)> {
    let last = parameters.len().checked_sub(2)?;
    let interval = parameters.partition_point(|&parameter| parameter <= u).saturating_sub(1).min(last);
    let (from, to) = (parameters[interval], parameters[interval + 1]);
    (to > from).then(|| (interval, ((u - from) / (to - from)).clamp(0.0, 1.0)))
}

/// Lane R2 row 2a (2026-10-18): `BREP_BLEND_AFFINE_READS=1`/`on` reads the
/// rails of a straight stripe on a PLANE at their window ends
/// ([`affine_rail_standoff`], [`exact_planar_trim`]'s read; the default since
/// its census: 98 + 144 cases identical in every field), `=0`/`off` samples
/// them as before; `set_affine_reads_for_test` forces it on a test's own
/// thread.
const AFFINE_READS_DEFAULT: bool = true;

pub(in crate::blend) fn affine_reads_on() -> bool {
    switch!(live "BREP_BLEND_AFFINE_READS", AFFINE_READS_DEFAULT)
}


/// A point of `surface` and its unit normal when the surface is AFFINE in
/// (u, v), its extension included: a plane (`analytic`) of degree 1 × 1 whose
/// 2 × 2 net is a parallelogram with equal weights, to `1e-12·(1 + scale)`.
/// Every point of such a surface, and of its linear extension, lies on the one
/// plane through the corner with that normal.
fn affine_plane(surface: &NurbsSurface) -> Option<(Vec3, Vec3)> {
    let (origin, _, _) = affine_plane_frame(surface)?;
    // The normal off the net's own corner differences.
    let net = &surface.control_points;
    let [p00, p01, p10] = [net[0][0].point().ok()?, net[0][1].point().ok()?, net[1][0].point().ok()?];
    Some((origin, p10.sub(p00).cross(p01.sub(p00)).normalized().ok()?))
}

/// [`affine_plane`]'s surface as `S(u, v) = origin + (u − u₀)·s_u + (v − v₀)·s_v`:
/// the net's corner and the two constant partial derivatives.
pub(in crate::blend) fn affine_plane_frame(surface: &NurbsSurface) -> Option<(Vec3, Vec3, Vec3)> {
    if !matches!(surface.analytic(), Some(crate::AnalyticSurface::Plane { .. })) || surface.degree_u != 1 || surface.degree_v != 1 {
        return None;
    }
    let net = &surface.control_points;
    if net.len() != 2 || net.iter().any(|row| row.len() != 2) {
        return None;
    }
    let weight = net[0][0].w;
    if !(weight > 0.0) || net.iter().flatten().any(|point| point.w != weight) {
        return None;
    }
    let [p00, p01, p10, p11] = [net[0][0].point().ok()?, net[0][1].point().ok()?, net[1][0].point().ok()?, net[1][1].point().ok()?];
    let scale = [p00, p01, p10, p11].iter().fold(0.0f64, |worst, point| worst.max(point.length()));
    if p11.sub(p10).sub(p01.sub(p00)).length() > 1e-12 * (1.0 + scale) {
        return None;
    }
    let [u0, u1] = surface.domain_u().ok()?;
    let [v0, v1] = surface.domain_v().ok()?;
    if !(u1 > u0 && v1 > v0) {
        return None;
    }
    Some((p00, p10.sub(p00).scale(1.0 / (u1 - u0)), p01.sub(p00).scale(1.0 / (v1 - v0))))
}

/// Whether `curve` is AFFINE in its parameter: degree 1, two control points of
/// equal weight (a `make_line`).
fn affine_curve(curve: &NurbsCurve) -> bool {
    curve.degree == 1
        && curve.control_points.len() == 2
        && curve.control_points[0].w > 0.0
        && curve.control_points[0].w == curve.control_points[1].w
}

/// The worst distance of an affine rail from an affine carrier over `window`,
/// read at the window's two ends: the signed distance is affine in the rail
/// parameter, so its magnitude is convex and its maximum is at an end. `None`
/// when either is not affine (the sampled reader's), or an end will not
/// evaluate.
pub(in crate::blend) fn affine_rail_standoff(rail: &NurbsCurve, carrier: &NurbsSurface, window: [f64; 2]) -> Option<f64> {
    if !affine_curve(rail) {
        return None;
    }
    let (origin, normal) = affine_plane(carrier)?;
    let mut worst = 0.0f64;
    for u in window {
        let point = rail.evaluate(u).ok()?;
        let off = point.sub(origin).dot(normal).abs();
        if !off.is_finite() {
            return None;
        }
        worst = worst.max(off);
    }
    Some(worst)
}

/// Lane N row 4 (2026-10-28): the exact revolution rows' reads in closed
/// form. `BREP_EXACT_REVOLUTION_READS=1`/`on` turns them on, `=0`/`off`
/// off; ON by default since lane N's promotion (2026-10-28).
const EXACT_REVOLUTION_READS_DEFAULT: bool = true;

pub(in crate::blend) fn exact_revolution_reads_on() -> bool {
    switch!(live "BREP_EXACT_REVOLUTION_READS", EXACT_REVOLUTION_READS_DEFAULT)
}


/// A bound on `|S(pcurve(s)) − rail(s)|` over the whole parameter range when
/// the shipped pair is an exact revolution row's (`exact_revolution_rows`):
///
/// * on the REVOLUTION carrier the pcurve is a uv line along one coordinate
///   and the rail is the carrier's parallel circle at the held coordinate,
///   continued to the line's range along its own conic pieces
///   (`extend_conic_ends`) and re-parameterised to `[0, 1]`; that curve is
///   rebuilt here from the pcurve's two ends and compared with the rail net
///   for net — identical bases, so the homogeneous control differences
///   bound the curves everywhere (`net_bound`);
/// * on an AFFINE plane the pcurve carries the rail's own knots and the
///   chart images of the rail's control points; the plane's image of the
///   pcurve net is compared with the rail net the same way.
///
/// `None` when the pair is neither (the sampled read runs). The rail's
/// standoff from the carrier and the pcurve's miss are each at most the
/// bound (a point's distance to a surface is at most its distance to any
/// point of it; the miss adds the standoff once more), so a bound within a
/// third of the pcurve floor stands the trim by the march trim's own rule.
pub(in crate::blend) fn exact_revolution_read_bound(rail: &NurbsCurve, carrier: &NurbsSurface, pcurve: &NurbsCurve) -> Option<f64> {
    if let Some((origin, s_u, s_v)) = affine_plane_frame(carrier) {
        // The plane's image of the pcurve net, weight for weight.
        if pcurve.degree != rail.degree || pcurve.knots != rail.knots || pcurve.control_points.len() != rail.control_points.len() {
            return None;
        }
        let ([u0, _], [v0, _]) = (carrier.domain_u().ok()?, carrier.domain_v().ok()?);
        let image: Vec<Vec4> = pcurve
            .control_points
            .iter()
            .map(|control| {
                let (u, v) = (control.x / control.w, control.y / control.w);
                let point = origin.add(s_u.scale(u - u0)).add(s_v.scale(v - v0));
                Vec4 { x: point.x * control.w, y: point.y * control.w, z: point.z * control.w, w: control.w }
            })
            .collect();
        return crate::blend::stations::net_bound(&rail.control_points, &image);
    }
    if !matches!(carrier.analytic(), Some(crate::AnalyticSurface::RuledRevolution { .. } | crate::AnalyticSurface::Revolution { .. } | crate::AnalyticSurface::Torus { .. } | crate::AnalyticSurface::Sphere { .. })) {
        return None;
    }
    if !affine_curve(pcurve) {
        return None;
    }
    let [q0, q1] = pcurve.domain().ok()?;
    let (a, b) = (pcurve.evaluate(q0).ok()?, pcurve.evaluate(q1).ok()?);
    let ([u0, u1], [v0, v1]) = (carrier.domain_u().ok()?, carrier.domain_v().ok()?);
    let (circle, from, to) = if a.y.to_bits() == b.y.to_bits() && a.x != b.x && a.y >= v0 && a.y <= v1 {
        (carrier.iso_curve_v(a.y).ok()?, a.x, b.x)
    } else if a.x.to_bits() == b.x.to_bits() && a.y != b.y && a.x >= u0 && a.x <= u1 {
        (carrier.iso_curve_u(a.x).ok()?, a.y, b.y)
    } else {
        return None;
    };
    let [c0, c1] = circle.domain().ok()?;
    let reversed = to < from;
    let circle = if reversed { circle.reversed().ok()? } else { circle };
    let on_curve = |along: f64| if reversed { c0 + c1 - along } else { along };
    let (lo, hi) = (on_curve(from), on_curve(to));
    if !(hi > lo) {
        return None;
    }
    let segment = super::support::extend_conic_ends(&circle, lo, hi).ok()?;
    let [r0, r1] = segment.domain().ok()?;
    let degree = segment.degree;
    let mut knots = segment.knots.clone();
    for knot in knots.iter_mut() {
        *knot = ((*knot - r0) / (r1 - r0)).clamp(0.0, 1.0);
    }
    for knot in knots.iter_mut().take(degree + 1) {
        *knot = 0.0;
    }
    let count = knots.len();
    for knot in knots.iter_mut().skip(count - degree - 1) {
        *knot = 1.0;
    }
    if rail.degree != degree || rail.knots != knots || rail.control_points.len() != segment.control_points.len() {
        return None;
    }
    crate::blend::stations::net_bound(&rail.control_points, &segment.control_points)
}

/// Each shipped rail's worst distance from its carrier over ITS OWN window
/// ([`shipped_window`]): both window ends exactly and the quarter points of
/// every station span inside it, each against the carrier's EXTENSION
/// (`extended_foot_distance`, seeded by the stations' own contact uv) --
/// a finite planar chart clamps its projection, so retained overshoot past a
/// vertex would read a miss that is not there. `(worst, samples per rail)`;
/// any unreadable sample (a failed evaluation, an infinite foot) makes the
/// worst NaN, which [`OpenRungReading::rails_read`] never admits.
pub(in crate::blend) fn rails_on_shipped_windows(
    rows: &FittedRows,
    parameters: &[f64],
    stations: &[Station],
    carriers: [&NurbsSurface; 2],
    windows: [Option<[f64; 2]>; 2],
) -> (f64, [usize; 2]) {
    read_rail_windows([&rows.cr, &rows.cs], parameters, stations, carriers, windows,
        rows.exact_extrusion && affine_reads_on(), certified_rail_bounds(rows, carriers), None)
}

/// An exact revolution row's rails, bounded in closed form
/// (`exact_revolution_read_bound`) when the reads lane is on: the standoff
/// over any window is at most the bound.
fn certified_rail_bounds(rows: &FittedRows, carriers: [&NurbsSurface; 2]) -> [Option<f64>; 2] {
    if rows.revolution_axis.is_none() || !exact_revolution_reads_on() {
        return [None, None];
    }
    [
        exact_revolution_read_bound(&rows.cr, carriers[0], &rows.cr_pcurve),
        exact_revolution_read_bound(&rows.cs, carriers[1], &rows.cs_pcurve),
    ]
}

fn read_rail_windows(
    rails: [&NurbsCurve; 2], parameters: &[f64], stations: &[Station],
    carriers: [&NurbsSurface; 2], windows: [Option<[f64; 2]>; 2], affine: bool,
    certified: [Option<f64>; 2],
    mut memo: Option<&mut RailFeet>,
) -> (f64, [usize; 2]) {
    let mut worst = 0.0_f64;
    let mut samples = [0usize; 2];
    for (side, (rail, carrier)) in [(rails[0], carriers[0]), (rails[1], carriers[1])].into_iter().enumerate() {
        let Some([low, high]) = windows[side] else { continue };
        // An exact extrusion's straight rail on a plane: the ends carry the
        // worst (`affine_rail_standoff`), the samples between them cannot.
        if let Some(off) = affine.then(|| affine_rail_standoff(rail, carrier, [low, high])).flatten() {
            crate::blend::record_count("count.read.affine_rails", 1);
            samples[side] += 2;
            if !worst.is_nan() && off > worst {
                worst = off;
            }
            continue;
        }
        // An exact revolution row's rail: its net bound against the carrier's
        // own circle (or the plane's image of its pcurve) is its standoff's
        // bound over every window.
        if let Some(bound) = certified[side] {
            crate::blend::record_count("count.read.revolution_rails", 1);
            samples[side] += 1;
            if !worst.is_nan() && bound > worst {
                worst = bound;
            }
            continue;
        }
        let mut at = vec![low, high];
        for pair in parameters.windows(2) {
            for fraction in [0.25, 0.5, 0.75] {
                let u = pair[0] + (pair[1] - pair[0]) * fraction;
                if u > low && u < high {
                    at.push(u);
                }
            }
        }
        for u in at {
            let cached = memo.as_ref().and_then(|memo| memo.values[side].get(&u.to_bits()).copied());
            let off = if let Some(off) = cached {
                if let Some(memo) = memo.as_mut() { memo.hits += 1; }
                crate::blend::record_count("count.read.rail_foot_reused", 1);
                off
            } else {
            if let Some(memo) = memo.as_mut() {
                memo.reads += 1;
                crate::blend::record_count("count.read.rail_foot_fresh", 1);
            }
            let off = interval_at(parameters, u)
                .and_then(|(interval, fraction)| {
                    let (left, right) = (&stations[interval], &stations[interval + 1]);
                    let (a, b) = if side == 0 { (left.uv1, right.uv1) } else { (left.uv2, right.uv2) };
                    let seed = [a[0] + fraction * (b[0] - a[0]), a[1] + fraction * (b[1] - a[1])];
                    let point = rail.evaluate(u).ok()?;
                    crate::blend::stations::extended_foot_distance(carrier, point, seed).ok()
                })
                .filter(|off| off.is_finite())
                .unwrap_or(f64::NAN);
            if off.is_finite() {
                if let Some(memo) = memo.as_mut() { memo.values[side].insert(u.to_bits(), off); }
            }
            off
            };
            samples[side] += 1;
            if !worst.is_nan() && (off.is_nan() || off > worst) {
                worst = off;
            }
        }
    }
    (worst, samples)
}

/// A construction-local rail reader. It owns an immutable snapshot of every
/// input to a foot reading; only the requested windows can change. Native
/// parameter bits identify a sample, so nearby endpoints never share a foot.
/// The network keeps this reader from its chosen rung through shipped-window
/// acceptance, then drops it. Failed/nonfinite feet are never reused.
pub(in crate::blend) struct RailWindowReader {
    rails: [NurbsCurve; 2],
    carriers: [NurbsSurface; 2],
    parameters: Vec<f64>,
    stations: Vec<Station>,
    affine: bool,
    certified: [Option<f64>; 2],
    feet: RailFeet,
}

#[derive(Default)]
struct RailFeet {
    values: [rustc_hash::FxHashMap<u64, f64>; 2],
    reads: u64,
    hits: u64,
}

impl RailWindowReader {
    pub(in crate::blend) fn new(rows: &FittedRows, parameters: &[f64], stations: &[Station], carriers: [&NurbsSurface; 2]) -> Self {
        Self {
            rails: [rows.cr.clone(), rows.cs.clone()],
            carriers: [carriers[0].clone(), carriers[1].clone()],
            parameters: parameters.to_vec(), stations: stations.to_vec(),
            affine: rows.exact_extrusion && affine_reads_on(), certified: certified_rail_bounds(rows, carriers), feet: RailFeet::default(),
        }
    }

    pub(in crate::blend) fn read(&mut self, windows: [Option<[f64; 2]>; 2]) -> (f64, [usize; 2]) {
        read_rail_windows([&self.rails[0], &self.rails[1]], &self.parameters, &self.stations,
            [&self.carriers[0], &self.carriers[1]], windows, self.affine, self.certified, Some(&mut self.feet))
    }
}

pub(in crate::blend) fn network_rail_reuse_on() -> bool {
    // The private halving diagnostic compares every requested foot against
    // its reference algorithm, including repeats. Preserve that coverage.
    std::env::var("BREP_NETWORK_RAIL_REUSE").map_or(true, |value| value != "0" && value != "off")
}

/// The JUNCTION stencil across the station between interval `left` and
/// `left + 1`: `left_nodes` (33 exact centres of `left`) from node 16 to 32,
/// then `right_nodes` (33 of `left + 1`) from node 1 to 16, the shared
/// station at node 16. Its native abscissae come from `station_ts` (the
/// edge parameters the centres were solved at): `None` when the two native
/// widths are bitwise equal -- uniform nodes, read by the ordinary reader --
/// else each node's t, interior nodes by the dense solves' own arithmetic
/// t_i + (t_{i+1} − t_i)·j/32 and the shared station at its STORED t. Err
/// when a station t is not finite or a width is not strictly positive.
pub(in crate::blend) fn junction_stencil(
    left_nodes: &[Vec3],
    right_nodes: &[Vec3],
    station_ts: &[f64],
    left: usize,
) -> Result<(Vec<Vec3>, Option<Vec<f64>>), String> {
    let half = OPEN_WALL_CENTRES / 2;
    if left_nodes.len() != OPEN_WALL_CENTRES + 1 || right_nodes.len() != OPEN_WALL_CENTRES + 1 || left + 2 >= station_ts.len() {
        return Err(format!("interval {left}: junction needs two {}-node intervals and their three station parameters", OPEN_WALL_CENTRES + 1));
    }
    let (t0, t1, t2) = (station_ts[left], station_ts[left + 1], station_ts[left + 2]);
    let (w_left, w_right) = (t1 - t0, t2 - t1);
    if !(t0.is_finite() && t1.is_finite() && t2.is_finite() && w_left > 0.0 && w_right > 0.0 && w_left.is_finite() && w_right.is_finite()) {
        return Err(format!("interval {left}: junction station parameters {t0}, {t1}, {t2} are not finite and strictly increasing"));
    }
    let nodes: Vec<Vec3> = left_nodes[half..].iter().chain(&right_nodes[1..=half]).copied().collect();
    if w_left.to_bits() == w_right.to_bits() {
        return Ok((nodes, None));
    }
    let abscissae: Vec<f64> = (half..=OPEN_WALL_CENTRES)
        .map(|j| if j == OPEN_WALL_CENTRES { t1 } else { t0 + w_left * j as f64 / OPEN_WALL_CENTRES as f64 })
        .chain((1..=half).map(|j| t1 + w_right * j as f64 / OPEN_WALL_CENTRES as f64))
        .collect();
    Ok((nodes, Some(abscissae)))
}

/// One wall probe on a stencil: the ordinary uniform reader when it has no
/// native abscissae (an interval's own nodes, or a junction of equal native
/// widths), the native-abscissa reader when it has them.
pub(in crate::blend) fn read_on_stencil(point: Vec3, nodes: &[Vec3], abscissae: Option<&[f64]>, step: usize, radius: f64) -> Result<(f64, f64), String> {
    match abscissae {
        None => crate::blend::chain::declared_wall_probe_why(point, nodes, step, radius),
        Some(ts) => crate::blend::chain::declared_wall_probe_at_why(point, nodes, ts, step, radius),
    }
}

/// Every interval's largest DECLARED wall reading (`chain::declared_wall_probe`,
/// reading + uncertainty) on the open fillet `rows`, against the exact ball
/// centres the march solved at j/32 of each interval ([`OpenDenseCentres`]),
/// or, for each unread interval, WHY: its station
/// interval and which component failed (no dense row, a dense row of the
/// wrong size, an exact centre the march did not solve, a wall point the
/// surface would not evaluate, a probe the declared reader declined, no probe
/// in the shipped part, or a non-finite reading). The cause is diagnostic.
///
/// Read on the DECLARED SHIPPED REGION `window` only: an interval it overlaps
/// is probed at its even stencil fractions that lie inside the window and at
/// each window end inside the interval; a probe outside the window is not on
/// the shipped wall and is not read. A window END whose nearest even stencil
/// node falls outside 2..=30 -- an end within a node of the interval's own
/// station, as the original bore's shipped spans were, 1e-16 from their
/// vertex stations -- has its foot at the interval's end node, where
/// [`crate::blend::chain::declared_wall_probe`] cannot read (the stencil
/// cannot follow a foot out of its own interval). It is read on a JUNCTION
/// stencil instead: the neighbouring interval's last half and this one's
/// first half (or this one's last half and the next one's first), the same
/// exact centres meeting at the shared station, with the probe at its
/// middle node. Two intervals of unequal native width (local insertions)
/// give the junction unequal node spacing, so it is then read by the
/// native-abscissa reader (`chain::declared_wall_probe_at_why`) on the
/// edge parameters the centres were solved at; of equal width, by the
/// ordinary reader. With no neighbour it is read as before.
/// The wall reading of an EXACT extrusion's rows, read directly: the wall is
/// the ball's sweep along the centre line, so every point of the shipped
/// surface stands exactly the radius off that line, and the reading is how
/// far it does not. One entry per interval overlapping `window`, probes at
/// the interval's quarters inside the window and at the window's ends, over
/// the section at k/8 (the ball-resolve reader's sections). Never for fitted
/// rows: those are read against the exact centres between their stations
/// ([`open_wall_readings_why`]).
pub(in crate::blend) fn exact_extrusion_wall_readings(
    rows: &FittedRows,
    parameters: &[f64],
    window: [f64; 2],
    radius: f64,
) -> Vec<Result<f64, String>> {
    let intervals = parameters.len().saturating_sub(1);
    let overlapped: Vec<usize> =
        (0..intervals).filter(|&interval| parameters[interval + 1] > window[0] && parameters[interval] < window[1]).collect();
    let line = match (rows.exact_extrusion, rows.center.as_ref()) {
        (true, Some(center)) => center.domain().and_then(|[a, b]| Ok((center.evaluate(a)?, center.evaluate(b)?))),
        (false, _) => Err("the rows are not an exact extrusion".to_string()),
        (true, None) => Err("the exact rows carry no centre line".to_string()),
    };
    let (anchor, direction) = match line.and_then(|(a, b)| b.sub(a).normalized().map(|d| (a, d))) {
        Ok(line) => line,
        Err(error) => return overlapped.iter().map(|interval| Err(format!("interval {interval}: {error}"))).collect(),
    };
    let Ok([v_low, v_high]) = rows.surface.domain_v() else {
        return overlapped.iter().map(|interval| Err(format!("interval {interval}: wall surface v domain unreadable"))).collect();
    };
    overlapped
        .into_iter()
        .map(|interval| -> Result<f64, String> {
            let (from, to) = (parameters[interval], parameters[interval + 1]);
            let inside = |u: f64| u >= window[0] && u <= window[1];
            let mut probes: Vec<f64> =
                (1..4).map(|step| from + (to - from) * step as f64 / 4.0).filter(|u| inside(*u)).collect();
            probes.extend(window.into_iter().filter(|end| *end > from && *end < to));
            if probes.is_empty() {
                return Err(format!("interval {interval}: no probe in its shipped part of [{:.17}, {:.17}]", window[0], window[1]));
            }
            let mut worst = 0.0_f64;
            for u in probes {
                for k in 1..OPEN_WALL_SECTIONS {
                    let v = v_low + (v_high - v_low) * k as f64 / OPEN_WALL_SECTIONS as f64;
                    let point = rows.surface.evaluate(u, v).map_err(|error| format!("interval {interval}: wall point (u {u:.9}, v {v:.6}) unreadable: {error}"))?;
                    let offset = point.sub(anchor);
                    let standoff = offset.sub(direction.scale(offset.dot(direction))).length();
                    let reading = (standoff - radius).abs();
                    if !reading.is_finite() {
                        return Err(format!("interval {interval}: wall point (u {u:.9}, v {v:.6}) reads {reading}"));
                    }
                    worst = worst.max(reading);
                }
            }
            Ok(worst)
        })
        .collect()
}

/// The wall reading of an EXACT REVOLUTION's rows
/// (`support::exact_revolution_rows`), read directly: the wall is the ball's
/// sweep along the centre CIRCLE, so every point of the shipped surface
/// stands exactly the radius off that circle, and the reading is how far it
/// does not. The distance to the circle is closed-form in the point's
/// meridian half-plane: `hypot(ρ − R_c, h − h_c)` for the point's radial
/// distance ρ and axial height h about the rows' axis. Same probes as
/// [`exact_extrusion_wall_readings`]: one entry per interval overlapping
/// `window`, the interval's quarters inside the window and the window's
/// ends, over the section at k/8.
pub(in crate::blend) fn exact_revolution_wall_readings(
    rows: &FittedRows,
    parameters: &[f64],
    window: [f64; 2],
    radius: f64,
) -> Vec<Result<f64, String>> {
    let intervals = parameters.len().saturating_sub(1);
    let overlapped: Vec<usize> =
        (0..intervals).filter(|&interval| parameters[interval + 1] > window[0] && parameters[interval] < window[1]).collect();
    let circle = match (rows.revolution_axis, rows.center.as_ref()) {
        (Some((origin, axis)), Some(center)) => center.domain().and_then(|[a, _]| center.evaluate(a)).map(|point| (origin, axis, point)),
        (None, _) => Err("the rows are not an exact revolution".to_string()),
        (Some(_), None) => Err("the exact rows carry no centre circle".to_string()),
    };
    let (origin, axis, centre_radius, centre_height) = match circle {
        Ok((origin, axis, point)) => {
            let offset = point.sub(origin);
            let height = offset.dot(axis);
            (origin, axis, offset.sub(axis.scale(height)).length(), height)
        }
        Err(error) => return overlapped.iter().map(|interval| Err(format!("interval {interval}: {error}"))).collect(),
    };
    let Ok([v_low, v_high]) = rows.surface.domain_v() else {
        return overlapped.iter().map(|interval| Err(format!("interval {interval}: wall surface v domain unreadable"))).collect();
    };
    overlapped
        .into_iter()
        .map(|interval| -> Result<f64, String> {
            let (from, to) = (parameters[interval], parameters[interval + 1]);
            let inside = |u: f64| u >= window[0] && u <= window[1];
            let mut probes: Vec<f64> =
                (1..4).map(|step| from + (to - from) * step as f64 / 4.0).filter(|u| inside(*u)).collect();
            probes.extend(window.into_iter().filter(|end| *end > from && *end < to));
            if probes.is_empty() {
                return Err(format!("interval {interval}: no probe in its shipped part of [{:.17}, {:.17}]", window[0], window[1]));
            }
            let mut worst = 0.0_f64;
            for u in probes {
                for k in 1..OPEN_WALL_SECTIONS {
                    let v = v_low + (v_high - v_low) * k as f64 / OPEN_WALL_SECTIONS as f64;
                    let point = rows.surface.evaluate(u, v).map_err(|error| format!("interval {interval}: wall point (u {u:.9}, v {v:.6}) unreadable: {error}"))?;
                    let offset = point.sub(origin);
                    let height = offset.dot(axis);
                    let radial = offset.sub(axis.scale(height)).length();
                    let standoff = (radial - centre_radius).hypot(height - centre_height);
                    let reading = (standoff - radius).abs();
                    if !reading.is_finite() {
                        return Err(format!("interval {interval}: wall point (u {u:.9}, v {v:.6}) reads {reading}"));
                    }
                    worst = worst.max(reading);
                }
            }
            Ok(worst)
        })
        .collect()
}

pub(in crate::blend) fn open_wall_readings_why(
    rows: &FittedRows,
    parameters: &[f64],
    stations: &[Station],
    window: [f64; 2],
    dense: &OpenDenseCentres,
    radius: f64,
) -> Vec<Result<f64, String>> {
    let intervals = parameters.len().saturating_sub(1);
    let overlapped: Vec<usize> =
        (0..intervals).filter(|&interval| parameters[interval + 1] > window[0] && parameters[interval] < window[1]).collect();
    let Ok([v_low, v_high]) = rows.surface.domain_v() else {
        return overlapped.iter().map(|interval| Err(format!("interval {interval}: wall surface v domain unreadable"))).collect();
    };
    // The exact centres of one interval at j/32, its stations at both ends.
    let nodes_of = |interval: usize| -> Result<Vec<Vec3>, String> {
        let inner = dense.centres.get(interval).ok_or_else(|| format!("interval {interval}: no dense centre row"))?;
        if dense.subdivisions != OPEN_WALL_CENTRES || inner.len() + 1 != OPEN_WALL_CENTRES {
            return Err(format!("interval {interval}: dense row {} centres at {} subdivisions", inner.len(), dense.subdivisions));
        }
        let mut centres = Vec::with_capacity(OPEN_WALL_CENTRES + 1);
        centres.push(stations[interval].center);
        for (j, centre) in inner.iter().enumerate() {
            centres.push(centre.ok_or_else(|| format!("interval {interval}: exact centre {} of {OPEN_WALL_CENTRES} unsolved", j + 1))?);
        }
        centres.push(stations[interval + 1].center);
        Ok(centres)
    };
    let half = OPEN_WALL_CENTRES / 2;
    // The junction stencil across the station between `left` and `left + 1`:
    // left's nodes half..=32, then right's nodes 1..=half; the station is node
    // `half`. With each node's NATIVE abscissa (the edge parameter its exact
    // centre was solved at, `OpenDenseCentres::station_ts`), and `None` when
    // the two intervals' native widths are bitwise equal: the nodes are then
    // uniform and the ordinary reader reads them, bit for bit.
    let junction = |left: usize| -> Result<(Vec<Vec3>, Option<Vec<f64>>), String> {
        if dense.station_ts.len() != parameters.len() {
            return Err(format!("interval {left}: junction without the native station parameters"));
        }
        junction_stencil(&nodes_of(left)?, &nodes_of(left + 1)?, &dense.station_ts, left)
    };
    overlapped
        .into_iter()
        .map(|interval| -> Result<f64, String> {
            let centres = nodes_of(interval)?;
            let (from, to) = (parameters[interval], parameters[interval + 1]);
            let inside = |u: f64| u >= window[0] && u <= window[1];
            // (u, stencil node, the stencil: None = this interval's own;
            // a junction's nodes with its native abscissae when unequal).
            let mut probes: Vec<(f64, usize, Option<(Vec<Vec3>, Option<Vec<f64>>)>)> = (2..OPEN_WALL_CENTRES)
                .step_by(2)
                .map(|step| (from + (to - from) * step as f64 / OPEN_WALL_CENTRES as f64, step))
                .filter(|(u, _)| inside(*u))
                .map(|(u, step)| (u, step, None))
                .collect();
            for end in window {
                if end > from && end < to {
                    let fraction = (end - from) / (to - from);
                    let nearest = 2 * ((fraction * OPEN_WALL_CENTRES as f64 / 2.0).round() as usize);
                    #[cfg(not(test))]
                    let junction_off = false;
                    if junction_off {
                        probes.push((end, nearest.clamp(2, OPEN_WALL_CENTRES - 2), None));
                    } else if nearest < 2 && interval > 0 {
                        probes.push((end, half, Some(junction(interval - 1)?)));
                    } else if nearest > OPEN_WALL_CENTRES - 2 && interval + 1 < intervals {
                        probes.push((end, half, Some(junction(interval)?)));
                    } else {
                        probes.push((end, nearest.clamp(2, OPEN_WALL_CENTRES - 2), None));
                    }
                }
            }
            if probes.is_empty() {
                return Err(format!("interval {interval}: no probe in its shipped part of [{:.17}, {:.17}]", window[0], window[1]));
            }
            let mut declared = 0.0_f64;
            for (u, step, stencil) in probes {
                let (nodes, abscissae, what) = match &stencil {
                    Some((nodes, None)) => (nodes, None, "junction"),
                    Some((nodes, Some(ts))) => (nodes, Some(ts), "native-abscissa junction"),
                    None => (&centres, None, "interval"),
                };
                for k in 1..OPEN_WALL_SECTIONS {
                    let v = v_low + (v_high - v_low) * k as f64 / OPEN_WALL_SECTIONS as f64;
                    let point = rows.surface.evaluate(u, v).map_err(|error| format!("interval {interval}: wall point (u {u:.9}, v {v:.6}) unreadable: {error}"))?;
                    // The interval's own stencil reads from the probe's node,
                    // then (declined) from where its fraction reaches along the
                    // centre path (a stationary section); a junction keeps its
                    // own dispatch.
                    let read = if stencil.is_none() {
                        crate::blend::chain::declared_wall_probe_located_why(point, nodes, step, (u - from) / (to - from), radius)
                    } else {
                        read_on_stencil(point, nodes, abscissae.map(|ts| ts.as_slice()), step, radius)
                    };
                    let (reading, uncertainty) = read.map_err(|why| {
                        format!(
                            "interval {interval}: declared reader declined probe node {step} of the {what} stencil, section {k}/{OPEN_WALL_SECTIONS} \
                             (u {u:.17}): {why}"
                        )
                    })?;
                    declared = declared.max(reading + uncertainty);
                }
            }
            if declared.is_finite() { Ok(declared) } else { Err(format!("interval {interval}: non-finite reading {declared}")) }
        })
        .collect()
}


/// A fresh id allocator seeded past the vertex, edge, face, loop and coedge IDs.
/// Solid and shell IDs are outside this allocation domain. Shared by
/// a whole group of stripes so their vertices, edges and coedges cannot
/// collide.
pub(in crate::blend) fn fresh_id_source(solid: &BrepSolid) -> impl FnMut() -> u64 {
    let mut next_id = solid
        .vertices
        .iter()
        .map(|vertex| vertex.id)
        .chain(solid.edges.iter().map(|edge| edge.id))
        .chain(
            solid
                .shells
                .iter()
                .flat_map(|shell| &shell.faces)
                .flat_map(|face| {
                    face.loops
                        .iter()
                        .map(|loop_record| loop_record.id)
                        .chain(face.loops.iter().flat_map(|loop_record| {
                            loop_record.coedges.iter().map(|coedge| coedge.id)
                        }))
                        .chain(std::iter::once(face.id))
                }),
        )
        .max()
        .unwrap_or(0)
        + 1;
    move || {
        let id = next_id;
        next_id += 1;
        id
    }
}


/// May a declined support refit fall back on the march's own trim pcurve?
/// Only when that trim, read independently as shipped
/// (`read_shipped_pcurve`: miss, rail standoff, samples, worst), meets the
/// floor ON its rail's branch: its miss PLUS the rail's standoff from the
/// carrier at the trim's foot within `floor`. A trim on another branch of the
/// carrier tracks its own foot perfectly (miss 0) while that foot stands far
/// off the rail, so a miss alone would ship it; an unreadable trim never
/// stands.
pub(in crate::blend) fn march_trim_stands(read: &Result<(f64, f64, usize, f64), KernelRefusal>, floor: f64) -> bool {
    matches!(read, Ok((miss, standoff, _, _)) if *miss + *standoff <= floor)
}

/// Slice 1b of the surgery lane (`exact_trim_on`): the support trim of a
/// straight rail on a PLANE, exact. The feet of the rail's two ends on the
/// plane (the kernel's closed-form projection) joined by a degree-1 pcurve
/// on the rail's own parameter range, so the surgery reads it at the rail's
/// parameter as it reads every trim; read once as shipped
/// (`read_shipped_pcurve`) and kept only when it stands within the pcurve
/// floor by the march trim's own rule (`march_trim_stands`: miss plus rail
/// standoff). `None` hands the trim to the refit road, as a march trim that
/// does not stand would be.
fn exact_planar_trim(rail: &NurbsCurve, surface: &NurbsSurface, d0: f64, d1: f64) -> Result<Option<NurbsCurve>, KernelRefusal> {
    if !matches!(surface.analytic(), Some(crate::AnalyticSurface::Plane { .. })) || !(d1 > d0) {
        return Ok(None);
    }
    let feet_timing = crate::blend::BlendTimer::start("timing.surgery.trim.exact.feet_ms");
    let mut control = Vec::with_capacity(2);
    for t in [d0, d1] {
        let point = rail.evaluate(t).or_refuse(KernelStage::Refine, "evaluate")?;
        let foot = crate::project_point_to_surface(surface, point).or_refuse(KernelStage::Refine, "project")?;
        control.push(crate::Vec4::from_point(crate::Vec3::new(foot.u, foot.v, 0.0), 1.0));
    }
    let line = NurbsCurve::new(1, vec![d0, d0, d1, d1], control).or_refuse(KernelStage::Refine, "new")?;
    feet_timing.stop();
    let read_timing = crate::blend::BlendTimer::start("timing.surgery.trim.exact.read_ms");
    let read = match affine_reads_on().then(|| affine_trim_read(rail, surface, &line, d0, d1)).flatten() {
        Some(read) => {
            crate::blend::record_count("count.read.affine_trims", 1);
            Ok(read)
        }
        None => crate::blend::track_fit::read_shipped_pcurve(surface, &|t| rail.evaluate(t), &line, d0, d1),
    };
    read_timing.stop();
    Ok(march_trim_stands(&read, crate::pcurve::PCURVE_REFINEMENT_TOLERANCE).then_some(line))
}

/// [`read_shipped_pcurve`](crate::blend::track_fit::read_shipped_pcurve)'s
/// `(miss, standoff, samples, worst)` for a degree-1 `pcurve` on an AFFINE
/// plane against an affine rail, read at `d0` and `d1` only: the pcurve's
/// image, the rail and the rail's foot on the plane are all affine in the
/// rail parameter, so the miss (image to foot) and the standoff (rail to foot)
/// are norms of affine functions, convex, and largest at an end. `None` when
/// any of the three is not affine, or an end will not evaluate.
fn affine_trim_read(rail: &NurbsCurve, surface: &NurbsSurface, pcurve: &NurbsCurve, d0: f64, d1: f64) -> Option<(f64, f64, usize, f64)> {
    if !affine_curve(rail) || !affine_curve(pcurve) {
        return None;
    }
    let (origin, normal) = affine_plane(surface)?;
    let (mut miss, mut standoff, mut worst) = (0.0f64, 0.0f64, d0);
    for t in [d0, d1] {
        let point = rail.evaluate(t).ok()?;
        let foot = point.sub(normal.scale(point.sub(origin).dot(normal)));
        let uv = pcurve.evaluate(t).ok()?;
        let image = crate::blend::track_fit::carrier_point(surface, uv.x, uv.y).ok()?;
        let (residual, off) = (image.sub(foot).length(), point.sub(foot).length());
        if !(residual.is_finite() && off.is_finite()) {
            return None;
        }
        if residual > miss {
            (miss, worst) = (residual, t);
        }
        standoff = standoff.max(off);
    }
    Some((miss, standoff, 2, worst))
}

/// Sew ONE stripe into `result`.
///
/// `solid` is the ORIGINAL topology every stripe was marched against (the
/// row-coincidence detection reads it); `result` is the shared evolving
/// solid, and `take_id` the shared id allocator — a group of stripes meeting
/// at a corner must create their rim vertices and cross-section arcs from one
/// counter, and must see each other's work.
///
/// Each end is either free (terminate on the face across the corner) or a
/// corner stop; see [`EndPlan`].  Pruning orphaned vertices is the caller's
/// job, once, after every stripe of the group is in.
pub(in crate::blend) fn build_open_surgery(
    solid: &BrepSolid,
    result: &mut BrepSolid,
    take_id: &mut dyn FnMut() -> u64,
    edge: &EdgeRecord,
    first: &BlendMate,
    second: &BlendMate,
    rows: FittedRows,
    ends: [EndPlan; 2],
    name: Option<&str>,
) -> Result<SewnStripe, KernelRefusal> {
    build_open_surgery_with_tail(solid, result, take_id, edge, first, second, rows, ends, name, [None, None])
}

/// [`build_open_surgery`] for a stripe whose finish end rolled onto tangent
/// neighbours (`crate::blend::rollover::stopped_end_tail`, switch, default
/// off): after the pristine sewing, each rolled side's rail is split at its
/// crossing vertex, the part past it and the cap's leg move from the mate's
/// loop into the neighbour's — where they replace the run of the crossed
/// tangent edge from the crossing to the sharp vertex, which is deleted —
/// and the part past it gets its pcurve on the neighbour. The mate keeps the
/// rail up to the crossing and the rest of the tangent edge. With no rolled
/// side the sewing is exactly [`build_open_surgery`]'s.
#[allow(clippy::too_many_arguments)]
pub(in crate::blend) fn build_open_surgery_with_tail(
    solid: &BrepSolid,
    result: &mut BrepSolid,
    take_id: &mut dyn FnMut() -> u64,
    edge: &EdgeRecord,
    first: &BlendMate,
    second: &BlendMate,
    rows: FittedRows,
    ends: [EndPlan; 2],
    name: Option<&str>,
    tail: [Option<crate::blend::rollover::TailSplice<'_>>; 2],
) -> Result<SewnStripe, KernelRefusal> {
    let [start_end, finish_end] = ends;

    // Trim the support rows to the crossing window.
    // A POLE end stops EXACTLY on the row's own end (the march puts the
    // pole's station there and overshoots no further), so there is nothing to
    // split off on that side.  Only equality: a stop PAST the row's end — a
    // free end's crossing the rows never reached — still fails the split and
    // refuses, rather than building a rail out to wherever the rows stop.
    let trim_row = |row: &NurbsCurve, a: f64, b: f64| -> Result<NurbsCurve, KernelRefusal> {
        let [low, high] = row.domain().or_refuse(KernelStage::Refine, "domain")?;
        let tail = if a == low { row.clone() } else { row.split(a).or_refuse(KernelStage::Refine, "split")?.1 };
        if b == high {
            return Ok(tail);
        }
        let (middle, _) = tail.split(b).or_refuse(KernelStage::Refine, "split")?;
        Ok(middle)
    };
    let kind = |plan: &EndPlan| match plan {
        EndPlan::Free(_) => "free",
        EndPlan::Corner(_) => "corner",
        EndPlan::Miter(_) => "miter",
        EndPlan::Cap(_) => "cap",
    };
    let describe_trim = |error: KernelRefusal| {
        error.with_message(|error| {
            format!(
                "{error} (edge {} rows trimmed for a {} start at cr {:.6}/cs {:.6} and a {} finish \
                 at cr {:.6}/cs {:.6})",
                edge.id,
                kind(&start_end),
                start_end.cr_parameter(),
                start_end.cs_parameter(),
                kind(&finish_end),
                finish_end.cr_parameter(),
                finish_end.cs_parameter()
            )
        })
    };
    // A rail whose two stops are one point within `consumed_band` has no strip
    // to trim: the blend takes that mate down to a single point along this
    // edge.  That is a FULL-WIDTH corner — the shared face of a miter whose
    // radius is the side face's width, every face of a star at full width, the
    // middle edge of a channel whose two corner setbacks meet.  No rail edge is
    // built; the mate loses the blended edge instead of having it replaced, and
    // the rail's two rim vertices are one vertex, which the orchestrator
    // identifies (`SewnStripe::identified`) once every stripe is in.  A stripe
    // free at BOTH ends has no corner to collapse onto, so it refuses.
    let band = consumed_band(solid);
    let exact_trim = crate::blend::exact_trim_on();
    let collapses = |row: &NurbsCurve, a: f64, b: f64| -> Result<bool, KernelRefusal> {
        let from = row.evaluate(a).or_refuse(KernelStage::Refine, "evaluate")?;
        Ok(row.evaluate(b).or_refuse(KernelStage::Refine, "evaluate")?.sub(from).length() <= band
            && row.evaluate(0.5 * (a + b)).or_refuse(KernelStage::Refine, "evaluate")?.sub(from).length() <= band)
    };
    let cr_collapsed = collapses(&rows.cr, start_end.cr_parameter(), finish_end.cr_parameter())?;
    let cs_collapsed = collapses(&rows.cs, start_end.cs_parameter(), finish_end.cs_parameter())?;
    if cr_collapsed || cs_collapsed {
        let unsupported = if start_end.free().is_some() && finish_end.free().is_some() {
            Some("neither end is a corner")
        } else if first.face.id == second.face.id {
            Some("both rails lie on one face")
        } else if matches!(start_end, EndPlan::Cap(_)) || matches!(finish_end, EndPlan::Cap(_)) {
            Some("a capped end has no corner to share the point with")
        } else {
            None
        };
        if let Some(reason) = unsupported {
            return Err(KernelRefusal::unsupported(KernelStage::Sew, super::RAIL_COLLAPSE_WHAT, format!(
                "{RAIL_COLLAPSE_UNSUPPORTED} edge {}'s rail shrinks to a point and {reason}",
                edge.id
            )));
        }
    }
    // The support pcurve the rows carry interpolates the stations' (u, v) and
    // is read nowhere between them: on the 20-degree crossing's notched open
    // exit arc it stood 2.98e-5 off the rail it trims (q1 = 9cd8c3163 + the
    // section-fit fix, read against the exact cylinders). Inside the crossing
    // window — on the face, never on a carrier's extension — it is refitted
    // to the trimmed rail's own projected track at the pcurve floor and read
    // again as shipped (`refit_closed_support_pcurve`, the closed edge's).
    // A rail that overruns a PLANE carrier's chart (the rib-base fin top's
    // rails run 0.303 past it) is on the plane's extension, which the chart
    // clamps: there the refit and both reads would measure the clamp, and the
    // trim is instead rebuilt from its edge on the widened chart and verified
    // at the floor by `fit_planar_charts_to_trims`, which every lane that
    // sews these stripes runs.
    let trim_rail = |collapsed: bool,
                     row: &NurbsCurve,
                     pcurve: &NurbsCurve,
                     surface: &NurbsSurface,
                     a: f64,
                     b: f64|
     -> Result<Option<(NurbsCurve, NurbsCurve)>, KernelRefusal> {
        if collapsed {
            return Ok(None);
        }
        let rows_timing = crate::blend::BlendTimer::start("timing.surgery.trim.rows_ms");
        let rail = trim_row(row, a, b).map_err(describe_trim)?;
        let interpolated = trim_row(pcurve, a, b)?;
        rows_timing.stop();
        {
            let overrun_timing = crate::blend::BlendTimer::start("timing.surgery.trim.overrun_ms");
            let [d0, d1] = rail.domain().or_refuse(KernelStage::Refine, "domain")?;
            let overrun = crate::blend::planar_chart_overrun(surface, &|t| rail.evaluate(t), d0, d1)?;
            overrun_timing.stop();
            if overrun.is_some_and(|excursion| excursion > band) {
                return Ok(Some((rail, interpolated)));
            }
        }
        let [d0, d1] = rail.domain().or_refuse(KernelStage::Refine, "domain")?;
        // Slice 1b (`exact_trim_on`): an exact extrusion row's rail on a
        // plane ships the exact line in uv, read once; the refit road below
        // is for what it declines.
        if exact_trim && rows.exact_extrusion {
            if let Some(line) = exact_planar_trim(&rail, surface, d0, d1)? {
                crate::blend::record_count("count.surgery.exact_trim", 1);
                return Ok(Some((rail, line)));
            }
        }
        // The exact REVOLUTION rows' own trims are exact by construction
        // (the carrier's uv line, the plane's affine image of the rail):
        // read once as shipped by the march trim's rule (`march_trim_stands`)
        // and kept; whatever does not stand takes the refit road below.
        if rows.revolution_axis.is_some() {
            // Lane N row 4: the pair bounded in closed form stands without a
            // sample (`exact_revolution_read_bound` on the UNCUT row and
            // pcurve, whose restrictions to [a, b] these are: miss + standoff
            // within three bounds).
            if exact_revolution_reads_on() {
                let cert_timing = crate::blend::BlendTimer::start("timing.surgery.trim.exact.cert_ms");
                let bound = exact_revolution_read_bound(row, surface, pcurve);
                cert_timing.stop();
                if bound.is_some_and(|bound| 3.0 * bound <= crate::pcurve::PCURVE_REFINEMENT_TOLERANCE) {
                    crate::blend::record_count("count.surgery.exact_revolution_trim_certified", 1);
                    return Ok(Some((rail, interpolated)));
                }
            }
            let read_timing = crate::blend::BlendTimer::start("timing.surgery.trim.exact.read_ms");
            let read = crate::blend::track_fit::read_shipped_pcurve(surface, &|t| rail.evaluate(t), &interpolated, d0, d1);
            read_timing.stop();
            if march_trim_stands(&read, crate::pcurve::PCURVE_REFINEMENT_TOLERANCE) {
                crate::blend::record_count("count.surgery.exact_revolution_trim", 1);
                return Ok(Some((rail, interpolated)));
            }
            crate::blend::record_count("count.surgery.exact_revolution_trim_declined", 1);
        }
        // The march's OWN trim pcurve, read independently at the rail's own
        // parameter against the same floor (`read_shipped_pcurve`): what a
        // declined refit may fall back to, and the standoff a refit on the
        // rail's own branch must match.
        // The feet of each pcurve against the rail are sought once and
        // shared between the reads below (`FootMemo`): the march trim's for
        // its own read and the branch comparison, the refit's for the
        // refit's own read, the branch comparison and the absolute bound.
        let mut march_feet = crate::blend::track_fit::FootMemo::default();
        let mut refit_feet = crate::blend::track_fit::FootMemo::default();
        let march_read_timing = crate::blend::BlendTimer::start("timing.surgery.trim.read.march_ms");
        let march_read = crate::blend::track_fit::read_shipped_pcurve_memo(surface, &|t| rail.evaluate(t), &interpolated, d0, d1, &mut march_feet);
        march_read_timing.stop();
        let floor = crate::pcurve::PCURVE_REFINEMENT_TOLERANCE;
        let declined = |why: KernelRefusal| -> Result<Option<(NurbsCurve, NurbsCurve)>, KernelRefusal> {
            // Kept ONLY when the march's trim stands on its own read
            // (`march_trim_stands`); otherwise the refusal stands.
            let stands = march_trim_stands(&march_read, floor);
            if stands {
                Ok(Some((rail.clone(), interpolated.clone())))
            } else {
                Err(why)
            }
        };
        let refit_timing = crate::blend::BlendTimer::start("timing.surgery.trim.refit_ms");
        let refit = super::closed::refit_closed_support_pcurve_read(&rail, &interpolated, surface, &mut refit_feet);
        refit_timing.stop();
        let _reads_timing = crate::blend::BlendTimer::start("timing.surgery.trim.reads_ms");
        match refit {
            Ok((fitted, refit_read)) => {
                // A refit tracks ITS OWN projected track; a track that took
                // another branch of the carrier tracks itself perfectly while
                // its foot stands far off the rail (the stationary-start
                // prism's coedge read 6.0 off its edge). Its foot standoff
                // must be the rail's own standoff on the march trim's branch.
                // BOTH reads are required: an unreadable refit read declines
                // the refit (the march trim then stands only on its own read
                // within the floor), and an unreadable march read cannot
                // establish that the refit is on the rail's branch, so it
                // refuses. Never shipped on an unknown.
                // The two are compared POINTWISE, at the same rail
                // parameters (`standoff_excess`), never as two maxima read at
                // each curve's own knots.
                // The refit's read is the shipped curve's own, handed back by
                // the refit (the same `read_shipped_pcurve` on the same
                // inputs, once).
                let (march_standoff, refit_standoff) = match (&march_read, Ok::<_, KernelRefusal>(refit_read)) {
                    (_, Err(error)) => return declined(error),
                    (Err(error), Ok(_)) => {
                        return Err(KernelRefusal::non_convergence(KernelStage::Refine, "support_refit_branch_unread", format!(
                            "blend: a support trim's refit cannot be shown to stand on its rail's branch: the march's own \
                             trim is unreadable ({})",
                            error.message
                        )));
                    }
                    (Ok((_, march_standoff, _, _)), Ok((_, refit_standoff, _, _))) => (*march_standoff, refit_standoff),
                };
                let excess_timing = crate::blend::BlendTimer::start("timing.surgery.trim.read.excess_ms");
                let excess = crate::blend::track_fit::standoff_excess_memo(
                    surface, &|t| rail.evaluate(t), &interpolated, &fitted, d0, d1, &mut march_feet, &mut refit_feet,
                );
                excess_timing.stop();
                let excess = match excess {
                    Ok(excess) => excess,
                    Err(error) => return declined(error),
                };
                if !(excess <= floor) {
                    return declined(KernelRefusal::non_convergence(KernelStage::Refine, "support_refit_branch", format!(
                        "blend: a support trim's refit stands up to {excess:.3e} farther off its rail than the march's own trim \
                         at the same rail parameter (largest standoffs {refit_standoff:.3e} and {march_standoff:.3e}): it took \
                         another branch of the carrier"
                    )));
                }
                // ABSOLUTE: the comparison above is relative to the march
                // trim, so a refit that follows an off-branch march trim
                // passes it. The refit's foot must also stand within the floor
                // of the rail's own deviation from its carrier, read by the
                // nearest-point projector at the same parameters
                // (`standoff_over_nearest`) -- the rail's approximation the
                // lane reports, never more.
                let nearest_timing = crate::blend::BlendTimer::start("timing.surgery.trim.read.nearest_ms");
                let absolute = crate::blend::track_fit::standoff_over_nearest_memo(surface, &|t| rail.evaluate(t), &fitted, d0, d1, &mut refit_feet);
                nearest_timing.stop();
                let absolute = match absolute {
                    Ok(absolute) => absolute,
                    Err(error) => return declined(error),
                };
                if !(absolute <= floor) {
                    return declined(KernelRefusal::non_convergence(KernelStage::Refine, "support_refit_off_rail", format!(
                        "blend: a support trim's refit stands up to {absolute:.3e} farther off its rail than the rail stands \
                         off its carrier: its foot is not the rail's nearest point"
                    )));
                }
                Ok(Some((rail, fitted)))
            }
            Err(error) => declined(error),
        }
    };
    let trim_timing = crate::blend::BlendTimer::start("timing.surgery.trim_rails_ms");
    let cr_rail = trim_rail(
        cr_collapsed,
        &rows.cr,
        &rows.cr_pcurve,
        &first.face.surface,
        start_end.cr_parameter(),
        finish_end.cr_parameter(),
    )?;
    let cs_rail = trim_rail(
        cs_collapsed,
        &rows.cs,
        &rows.cs_pcurve,
        &second.face.surface,
        start_end.cs_parameter(),
        finish_end.cs_parameter(),
    )?;
    trim_timing.stop();
    // A collapsed rail's domain is its two stops, which are one point.
    let cr_domain = match &cr_rail {
        Some((cr, _)) => cr.domain().or_refuse(KernelStage::Refine, "domain")?,
        None => [start_end.cr_parameter(), finish_end.cr_parameter()],
    };
    let cs_domain = match &cs_rail {
        Some((cs, _)) => cs.domain().or_refuse(KernelStage::Refine, "domain")?,
        None => [start_end.cs_parameter(), finish_end.cs_parameter()],
    };
    // A rail's end points, read off the trimmed rail or, collapsed, the row.
    let cr_at = |parameter: f64| match &cr_rail {
        Some((cr, _)) => cr.evaluate(parameter),
        None => rows.cr.evaluate(parameter),
    };
    let cs_at = |parameter: f64| match &cs_rail {
        Some((cs, _)) => cs.evaluate(parameter),
        None => rows.cs.evaluate(parameter),
    };
    let old_start = edge.start_vertex_id;
    let old_finish = edge.end_vertex_id;

    // Classify each of the four support crossings.  A crossing that lands at
    // the FAR endpoint of the boundary edge it meets (the end away from the
    // corner) means a prior fillet already consumed that whole boundary: the
    // new blend reuses the existing vertex there and the boundary edge is
    // deleted (its role is taken over by the new transverse curve on the
    // prior blend face).  An interior crossing is the pristine case — a fresh
    // vertex with the boundary trimmed to it.
    //
    // Consumed means within `consumed_band` — the slack `check_support_extent`
    // refuses past, so a rail between the two is not possible.
    let classify =
        |boundary_id: u64, boundary_param: f64, corner: u64| -> Result<RimResolution, KernelRefusal> {
            let boundary = result
                .edges
                .iter()
                .find(|candidate| candidate.id == boundary_id)
                .ok_or(KernelRefusal::internal(KernelStage::Sew, "end_boundary_edge", "blend: end boundary edge missing during surgery"))?;
            let span = (boundary.t1 - boundary.t0).abs().max(1e-12);
            let far_vertex = if boundary.start_vertex_id == corner {
                Some((boundary.end_vertex_id, boundary.t1, boundary.t0))
            } else if boundary.end_vertex_id == corner {
                Some((boundary.start_vertex_id, boundary.t0, boundary.t1))
            } else {
                None
            };
            if let Some((far_id, far_t, near_t)) = far_vertex {
                // A consumed boundary is a COINCIDENCE — a prior fillet's rim
                // vertex is exactly where this rail crosses — and the operands
                // were healed before the march, so it holds to solver
                // precision.  It is not "near the far end": a fillet whose
                // radius is 1e-3 short of the face's width crosses 1e-3 from
                // the far vertex and must keep that sliver, or the face is
                // snapped a full 1e-3 out of true (the oversized-radius limit
                // cases and issue 1177 measure exactly that).  Measured in
                // model units, never in the boundary's parameter — a unit
                // parameter on a 20-long edge would call 2e-3 a coincidence.
                let far_point = result
                    .vertices
                    .iter()
                    .find(|candidate| candidate.id == far_id)
                    .map(|candidate| candidate.point)
                    .ok_or(KernelRefusal::internal(KernelStage::Sew, "far_vertex", "blend: consumed boundary far vertex missing"))?;
                let crossing = boundary.curve.evaluate_extended(boundary_param).or_refuse(KernelStage::Refine, "evaluate_extended")?;
                let _ = span;
                let past_far = crossing.sub(far_point).length();
                if past_far <= band {
                    // Sanity: the reused vertex must exist.
                    if !result
                        .vertices
                        .iter()
                        .any(|candidate| candidate.id == far_id)
                    {
                        return Err(KernelRefusal::internal(KernelStage::Sew, "far_vertex", "blend: consumed boundary far vertex missing"));
                    }
                    return Ok(RimResolution::Consumed(far_id));
                }
                // A crossing BEYOND the far vertex is a rail that runs off
                // its face: the blend is wider than the face by more than the
                // band a snap may close, and trimming the boundary there would
                // extend it into air.  `check_support_extent` refuses this
                // before the march on every face the exact tool understands;
                // this is the refusal for the rest.  It is not the whole
                // answer: a crossing the solve CLAMPS to the far vertex reads
                // as consumed here, and `detect_row_coincidence` is what sees
                // that row leave the face.  Measured on the census: it fires on
                // a wall wider than its closed chain's arc (0.5 past) and on the
                // r = 10.001 pinch past an earlier round (2e-3 past), both of
                // which refused later in the surgery before.
                if (boundary_param - far_t) * (far_t - near_t) > 0.0 {
                    return Err(KernelRefusal::unsupported(KernelStage::Sew, "blend_wider_than_face", format!(
                        "{BLEND_WIDER_THAN_FACE} its rail crosses edge {boundary_id} {past_far:.3e} \
                         past that edge's far vertex, and a rail within {band:.3e} of it is the \
                         most this surgery snaps onto the edge"
                    )));
                }
            }
            Ok(RimResolution::Fresh)
        };
    // A CORNER end has no boundary to classify: the ball's tangency vertex is
    // the rim, and the boundary edge that used to reach the sharp corner is
    // the neighbouring stripe's own blended edge, which that stripe replaces.
    let resolve_side = |plan: &EndPlan,
                        side_first: bool,
                        corner: u64|
     -> Result<RimResolution, KernelRefusal> {
        if let Some(vertex) = plan.planned_rim(side_first) {
            return Ok(RimResolution::Corner(vertex));
        }
        match plan {
            EndPlan::Free(free) => classify(
                if side_first {
                    free.first_edge_id
                } else {
                    free.second_edge_id
                },
                if side_first {
                    free.first_edge_parameter
                } else {
                    free.second_edge_parameter
                },
                corner,
            ),
            EndPlan::Corner(_) | EndPlan::Miter(_) | EndPlan::Cap(_) => {
                Err(KernelRefusal::internal(KernelStage::Sew, "rim_vertices", "blend: planned end without rim vertices"))
            }
        }
    };
    let crossings_timing = crate::blend::BlendTimer::start("timing.surgery.crossings_ms");
    let start_first = resolve_side(&start_end, true, old_start)?;
    let start_second = resolve_side(&start_end, false, old_start)?;
    let finish_first = resolve_side(&finish_end, true, old_finish)?;
    let finish_second = resolve_side(&finish_end, false, old_finish)?;
    crossings_timing.stop();
    let _topology_timing = crate::blend::BlendTimer::start("timing.surgery.topology_ms");

    // Manifold pairing: the blend's use of the first support row must oppose
    // F1's use of the blended edge (see the blend-loop construction below).
    let first_use_forward = first.coedge.forward;
    let blend_cr_forward = !first_use_forward;

    // Curve-level coincidence (OCCT PR #1449 lesson 3): when BOTH of a mate's
    // crossings are endpoint-consumed AND the trimmed support row retraces the
    // existing edge joining the two far vertices, that mate face is FULLY
    // consumed — the surgery sews the blend face straight onto the existing
    // edge and drops the zero-width face, instead of creating a coincident
    // fresh support edge that would leave a sliver strip (the r = face-width
    // class).  Each side is detected independently; any ambiguity keeps the
    // pristine path (fail-safe).  `row_traversed_from_start`: the blend loop
    // walks cr in station order iff `blend_cr_forward`, and cs in the
    // OPPOSITE order (see the two loop branches below).
    // A stripe with a CORNER end has no boundary edges at that end to be
    // coincident with, so the detection only runs on stripes that are free at
    // both ends.  (Fail-safe: not detecting a coincidence keeps the pristine
    // path, which is what a corner stop wants anyway.)
    let (sew_first, sew_second) = match (start_end.free(), finish_end.free(), &cr_rail, &cs_rail) {
        (Some(start_free), Some(finish_free), Some((cr, _)), Some((cs, _))) => (
            detect_row_coincidence(
                solid,
                first,
                edge.id,
                start_free.first_edge_id,
                finish_free.first_edge_id,
                start_first.is_consumed(),
                start_first.existing().unwrap_or(0),
                finish_first.is_consumed(),
                finish_first.existing().unwrap_or(0),
                cr,
                blend_cr_forward,
                band,
            )?,
            detect_row_coincidence(
                solid,
                second,
                edge.id,
                start_free.second_edge_id,
                finish_free.second_edge_id,
                start_second.is_consumed(),
                start_second.existing().unwrap_or(0),
                finish_second.is_consumed(),
                finish_second.existing().unwrap_or(0),
                cs,
                !blend_cr_forward,
                band,
            )?,
        ),
        _ => (None, None),
    };
    // One face hosting both sides (the blended edge used twice by one face)
    // cannot be dropped for one side while the other still splices into it —
    // ambiguous, keep the pristine path for both.
    let (sew_first, sew_second): (Option<RowSew>, Option<RowSew>) =
        if first.face.id == second.face.id {
            (None, None)
        } else {
            (sew_first, sew_second)
        };

    // Resolve the four rim vertices: reuse the existing vertex for a consumed
    // side, else create a fresh vertex at the support-row endpoint.
    let mut resolve = |rim: &RimResolution, point: Result<Vec3, String>| -> Result<u64, KernelRefusal> {
        match rim.existing() {
            Some(id) => Ok(id),
            None => {
                let id = take_id();
                result.vertices.push(VertexRecord { id, point: point.or_refuse(KernelStage::Refine, "point")? });
                Ok(id)
            }
        }
    };
    let w1a = resolve(&start_first, cr_at(cr_domain[0]))?;
    let w1b = resolve(&finish_first, cr_at(cr_domain[1]))?;
    let w2a = resolve(&start_second, cs_at(cs_domain[0]))?;
    let w2b = resolve(&finish_second, cs_at(cs_domain[1]))?;
    // How far the consumed rims and sewn rows moved a rail onto what already
    // existed — what `check_snap_closure` measures the body against.
    let mut snap = [&sew_first, &sew_second]
        .into_iter()
        .flatten()
        .fold(0.0f64, |worst, sew| worst.max(sew.deviation));
    for (rim, vertex, row_end) in [
        (&start_first, w1a, cr_at(cr_domain[0]).or_refuse(KernelStage::Refine, "cr_at")?),
        (&finish_first, w1b, cr_at(cr_domain[1]).or_refuse(KernelStage::Refine, "cr_at")?),
        (&start_second, w2a, cs_at(cs_domain[0]).or_refuse(KernelStage::Refine, "cs_at")?),
        (&finish_second, w2b, cs_at(cs_domain[1]).or_refuse(KernelStage::Refine, "cs_at")?),
    ] {
        if !rim.is_consumed() {
            continue;
        }
        let point = result
            .vertices
            .iter()
            .find(|candidate| candidate.id == vertex)
            .ok_or(KernelRefusal::internal(KernelStage::Sew, "rim_vertex", "blend: consumed rim vertex missing"))?
            .point;
        snap = snap.max(row_end.sub(point).length());
    }

    // Support edges: a sewn side reuses the EXISTING coincident edge (no
    // fresh edge — OCCT's `SetExistingEdge` move); a pristine side gets the
    // fitted row as a fresh edge.
    // A collapsed side builds no edge at all.
    let cr_edge_id = match (&sew_first, &cr_rail) {
        (Some(sew), _) => Some(sew.edge_id),
        (None, None) => None,
        (None, Some((cr, _))) => {
            let id = take_id();
            result.edges.push(EdgeRecord {
                id,
                curve: cr.clone(),
                t0: cr_domain[0],
                t1: cr_domain[1],
                start_vertex_id: w1a,
                end_vertex_id: w1b,
                degenerate: false,
                name: None,
            });
            Some(id)
        }
    };
    let cs_edge_id = match (&sew_second, &cs_rail) {
        (Some(sew), _) => Some(sew.edge_id),
        (None, None) => None,
        (None, Some((cs, _))) => {
            let id = take_id();
            result.edges.push(EdgeRecord {
                id,
                curve: cs.clone(),
                t0: cs_domain[0],
                t1: cs_domain[1],
                start_vertex_id: w2a,
                end_vertex_id: w2b,
                degenerate: false,
                name: None,
            });
            Some(id)
        }
    };
    // The edge closing each end of the blend face: the §6.9 transverse curve
    // on the end face for a free end, or — at a corner — the end
    // cross-section arc the corner patch was already given, committed by the
    // caller and merely referenced here.
    let mut commit_end_edge = |plan: &EndPlan,
                               first_rim: u64,
                               second_rim: u64|
     -> Result<Option<u64>, KernelRefusal> {
        match plan {
            EndPlan::Corner(_) | EndPlan::Miter(_) | EndPlan::Cap(_) => Ok(None),
            EndPlan::Free(free) => {
                let id = take_id();
                let domain = free.transverse_curve.domain().or_refuse(KernelStage::Refine, "domain")?;
                result.edges.push(EdgeRecord {
                    id,
                    curve: free.transverse_curve.clone(),
                    t0: domain[0],
                    t1: domain[1],
                    start_vertex_id: first_rim,
                    end_vertex_id: second_rim,
                    degenerate: false,
                    name: None,
                });
                Ok(Some(id))
            }
        }
    };
    let transverse_a_id = commit_end_edge(&start_end, w1a, w2a)?;
    let transverse_b_id = commit_end_edge(&finish_end, w1b, w2b)?;

    // Replace the blended edge in each PRISTINE mate's loop — the blend
    // face's loop direction is forced by manifold pairing with F1's use of
    // the blended edge (`first_use_forward`, captured above).  A sewn mate is
    // dropped whole below; nothing to splice.  A COLLAPSED side has no rail to
    // put there: the mate simply loses the blended edge, and its loop closes
    // through the rim vertices the orchestrator identifies.
    for (mate, rail, sewn) in [
        (first, cr_edge_id.zip(cr_rail.as_ref()), sew_first.is_some()),
        (second, cs_edge_id.zip(cs_rail.as_ref()), sew_second.is_some()),
    ] {
        if sewn {
            continue;
        }
        let face = result
            .shells
            .iter_mut()
            .flat_map(|shell| &mut shell.faces)
            .find(|face| face.id == mate.face.id)
            .ok_or(KernelRefusal::internal(KernelStage::Sew, "mate_face", "blend: mate face lost during surgery"))?;
        let loop_record = &mut face.loops[mate.loop_index];
        let position = loop_record
            .coedges
            .iter()
            .position(|coedge| coedge.edge_id == edge.id)
            .ok_or(KernelRefusal::internal(KernelStage::Sew, "edge_coedge", "blend: edge coedge lost during surgery"))?;
        let Some((new_edge_id, (_, pcurve_forward))) = rail else {
            loop_record.coedges.remove(position);
            continue;
        };
        let old_forward = loop_record.coedges[position].forward;
        loop_record.coedges[position] = CoedgeRecord {
            id: loop_record.coedges[position].id,
            edge_id: new_edge_id,
            forward: old_forward,
            pcurve: if old_forward {
                pcurve_forward.clone()
            } else {
                pcurve_forward.reversed().or_refuse(KernelStage::Refine, "reversed")?
            },
        };
    }

    // A capped end's legs: on each mate, the leg runs from the rail's rim
    // vertex to the sharp vertex, where the continuing edge still starts.
    // It goes between the rail coedge and that continuing coedge, with the
    // sense that walks rim -> vertex when the rail ends at the rim.
    for (plan, at_start) in [(&start_end, true), (&finish_end, false)] {
        let EndPlan::Cap(cap) = plan else {
            continue;
        };
        for (mate, rail_edge_id, leg, rim) in [
            (first, cr_edge_id, &cap.first_leg, if at_start { w1a } else { w1b }),
            (second, cs_edge_id, &cap.second_leg, if at_start { w2a } else { w2b }),
        ] {
            // Refused above: a capped end never meets a collapsed rail.
            let rail_edge_id = rail_edge_id.ok_or(KernelRefusal::internal(KernelStage::Sew, "end_rail", "blend: a capped end lost its rail"))?;
            let face = result
                .shells
                .iter_mut()
                .flat_map(|shell| &mut shell.faces)
                .find(|face| face.id == mate.face.id)
                .ok_or(KernelRefusal::internal(KernelStage::Sew, "mate_face", "blend: mate face lost during cap splice"))?;
            let loop_record = &mut face.loops[mate.loop_index];
            let count = loop_record.coedges.len();
            let rail_at = loop_record
                .coedges
                .iter()
                .position(|coedge| coedge.edge_id == rail_edge_id)
                .ok_or(KernelRefusal::internal(KernelStage::Sew, "rail_coedge", "blend: rail coedge lost during cap splice"))?;
            // Which side of the rail coedge is this end?  The rail coedge
            // traverses rim-to-rim; the capped end is at its traversal END
            // when walking forward means station order and this is the
            // finish end, etc.  Decide by geometry: the neighbour whose
            // traversal touches the sharp vertex.
            let rail_forward = loop_record.coedges[rail_at].forward;
            let end_is_traversal_end = if rail_forward { !at_start } else { at_start };
            let (insert_at, forward, pcurve) = if end_is_traversal_end {
                // rail ... rim -> [leg rim->vertex] -> continuing edge
                (rail_at + 1, true, leg.1.clone())
            } else {
                // continuing edge -> [leg vertex->rim] -> rim ... rail
                (rail_at, false, leg.1.reversed().or_refuse(KernelStage::Refine, "reversed")?)
            };
            let _ = (count, rim);
            loop_record.coedges.insert(
                insert_at,
                CoedgeRecord {
                    id: take_id(),
                    edge_id: leg.0,
                    forward,
                    pcurve,
                },
            );
        }
    }

    // Drop consumed boundary edges from the mate loop they share with the
    // blended edge: the fresh support coedge already begins at the reused far
    // vertex, so removing the fully-covered boundary keeps the loop closed.
    // A SEWN mate's whole loop collapses (the face is dropped below), so its
    // consumed boundaries are only recorded for deletion, never spliced.
    let mut consumed_edges: Vec<u64> = Vec::new();
    let start_free = start_end.free();
    let finish_free = finish_end.free();
    for (mate, sewn, sides) in [
        (
            first,
            sew_first.is_some(),
            [
                (&start_first, start_free.map(|free| free.first_edge_id)),
                (&finish_first, finish_free.map(|free| free.first_edge_id)),
            ],
        ),
        (
            second,
            sew_second.is_some(),
            [
                (&start_second, start_free.map(|free| free.second_edge_id)),
                (&finish_second, finish_free.map(|free| free.second_edge_id)),
            ],
        ),
    ] {
        for (cross, boundary_id) in sides {
            if !cross.is_consumed() {
                continue;
            }
            let Some(boundary_id) = boundary_id else {
                continue;
            };
            if !sewn {
                let face = result
                    .shells
                    .iter_mut()
                    .flat_map(|shell| &mut shell.faces)
                    .find(|face| face.id == mate.face.id)
                    .ok_or(KernelRefusal::internal(KernelStage::Sew, "mate_face", "blend: mate face lost during surgery"))?;
                face.loops[mate.loop_index]
                    .coedges
                    .retain(|coedge| coedge.edge_id != boundary_id);
            }
            if !consumed_edges.contains(&boundary_id) {
                consumed_edges.push(boundary_id);
            }
        }
    }
    // Lesson 7: zero-span leftovers of a collapsing loop go with their face.
    for sew in [&sew_first, &sew_second].into_iter().flatten() {
        for id in &sew.collapsed_edges {
            if !consumed_edges.contains(id) {
                consumed_edges.push(*id);
            }
        }
    }

    // Locate the corner junction on each end face BEFORE trimming (the two
    // boundary coedges that meet at the old corner vertex, stepping over the
    // pole between them where the end face's apex IS that vertex) — edge ids
    // alone are ambiguous on two-coedge cap loops, so the meeting must be at
    // the corner.
    // (end, transverse_id, corner, first_consumed, second_consumed) -> plan.
    // Only FREE ends appear here: a corner stop rebuilds nothing on a third
    // face, because the corner patch is what closes it.
    let mut end_plans = Vec::with_capacity(2);
    for (end, transverse_id, corner, first_consumed, second_consumed) in [
        (
            start_end.free(),
            transverse_a_id,
            old_start,
            start_first.is_consumed(),
            start_second.is_consumed(),
        ),
        (
            finish_end.free(),
            transverse_b_id,
            old_finish,
            finish_first.is_consumed(),
            finish_second.is_consumed(),
        ),
    ] {
        let (Some(end), Some(transverse_id)) = (end, transverse_id) else {
            continue;
        };
        let face = result
            .shells
            .iter()
            .flat_map(|shell| &shell.faces)
            .find(|face| face.id == end.end_face_id)
            .ok_or(KernelRefusal::internal(KernelStage::Sew, "end_face", "blend: end face lost during surgery"))?;
        // Key on stable coedge ids (not indices) so processing one end can't
        // invalidate another that shares this face.
        let Some(located) = locate_end_corner(
            &result,
            face,
            corner,
            end.first_edge_id,
            end.second_edge_id,
        ) else {
            return Err(KernelRefusal::internal(KernelStage::Sew, "end_face_corner", "blend: end-face corner (adjacent boundary coedges) not found"));
        };
        // The apex the transverse curve cuts off goes with the boundaries the
        // blend consumes.
        for pole in &located.pole_edge_ids {
            if !consumed_edges.contains(pole) {
                consumed_edges.push(*pole);
            }
        }
        end_plans.push((
            end.end_face_id,
            transverse_id,
            end.transverse_end_pcurve.clone(),
            located,
            first_consumed,
            second_consumed,
        ));
    }

    // Trim the boundary edges at their support crossings.  A consumed side is
    // deleted instead of trimmed; a CORNER side touches no boundary at all.
    for (rim_resolution, boundary, corner, rim) in [
        (
            &start_first,
            start_free.map(|free| (free.first_edge_id, free.first_edge_parameter)),
            old_start,
            w1a,
        ),
        (
            &start_second,
            start_free.map(|free| (free.second_edge_id, free.second_edge_parameter)),
            old_start,
            w2a,
        ),
        (
            &finish_first,
            finish_free.map(|free| (free.first_edge_id, free.first_edge_parameter)),
            old_finish,
            w1b,
        ),
        (
            &finish_second,
            finish_free.map(|free| (free.second_edge_id, free.second_edge_parameter)),
            old_finish,
            w2b,
        ),
    ] {
        if !rim_resolution.trims_boundary() {
            continue;
        }
        let Some((boundary_id, boundary_param)) = boundary else {
            continue;
        };
        trim_edge_at(result, boundary_id, boundary_param, corner, rim)?;
    }

    // Rebuild each end face's corner: drop the consumed boundary coedge(s) and
    // splice in the transverse coedge between the (trimmed) boundaries.
    for (end_face, transverse_id, transverse_end_pcurve, located, first_consumed, second_consumed) in
        end_plans
    {
        let forward = located.x_is_first;
        let pcurve = if forward {
            transverse_end_pcurve
        } else {
            transverse_end_pcurve.reversed().or_refuse(KernelStage::Refine, "reversed")?
        };
        let (x_consumed, y_consumed) = if located.x_is_first {
            (first_consumed, second_consumed)
        } else {
            (second_consumed, first_consumed)
        };
        let transverse_coedge = CoedgeRecord {
            id: take_id(),
            edge_id: transverse_id,
            forward,
            pcurve,
        };
        splice_end_corner(
            result,
            end_face,
            &located,
            transverse_coedge,
            x_consumed,
            y_consumed,
        )?;
    }

    // Blend face: cr fwd -> TB -> cs rev -> TA rev in (t, z) space.  Both rows
    // share one parameterisation, so a collapsed cr reads its sense across the
    // strip the cs rail still spans (a stripe with BOTH rails collapsed has no
    // area; the orchestrator drops it with the edges that bound it).
    let (mid_u, station_uv) = match (&cr_rail, &cs_rail) {
        (Some((_, cr_pcurve)), _) => {
            let mid_u = (cr_domain[0] + cr_domain[1]) * 0.5;
            (mid_u, cr_pcurve.evaluate(mid_u).or_refuse(KernelStage::Refine, "evaluate")?)
        }
        (None, Some(_)) => {
            let mid_u = (cs_domain[0] + cs_domain[1]) * 0.5;
            (mid_u, rows.cr_pcurve.evaluate(mid_u).or_refuse(KernelStage::Refine, "evaluate")?)
        }
        (None, None) => (cr_domain[0], rows.cr_pcurve.evaluate(cr_domain[0]).or_refuse(KernelStage::Refine, "evaluate")?),
    };
    let blend_normal = raw_normal(&rows.surface, mid_u, 0.0)?;
    let n1 = raw_normal(&first.face.surface, station_uv.x, station_uv.y)?;
    let out1 = if first.face.same_sense {
        n1
    } else {
        n1.scale(-1.0)
    };
    let same_sense = blend_normal.dot(out1) >= 0.0;
    let loop_id = take_id();
    // Traversal senses of the blend's support coedges.  A fresh support edge
    // is parameterized in station order, so the sense is the loop's walking
    // direction (`blend_cr_forward` for cr, its opposite for cs); a SEWN
    // side's sense comes from the detection (the existing edge's own
    // parameter direction relative to that same walk — equal to the dropped
    // face's old sense, preserving manifold pairing with the survivor).  The
    // pcurves always follow the LOOP's walking direction and are unchanged.
    let cr_forward = sew_first
        .as_ref()
        .map_or(blend_cr_forward, |sew| sew.forward);
    let cs_forward = sew_second
        .as_ref()
        .map_or(!blend_cr_forward, |sew| sew.forward);
    // The two end slots of the blend loop.  `natural_forward` is the sense the
    // loop walks a transverse edge stored first-rim -> second-rim; a CORNER
    // arc was already committed in the walk direction (so the corner patch can
    // take it the other way round), so it is always used forward.
    // A slot is one coedge for a free end (the transverse curve) or a corner
    // end (the section arc), and one or two for a miter end (the seam, then
    // the sibling's section when the seam left the sibling first).  A corner
    // arc was committed in the walk direction; a miter edge records its own
    // sense along the walk.
    let mut end_slot = |plan: &EndPlan,
                        free_edge_id: Option<u64>,
                        natural_forward: bool|
     -> Result<Vec<CoedgeRecord>, KernelRefusal> {
        match plan {
            EndPlan::Corner(end) => Ok(vec![CoedgeRecord {
                id: take_id(),
                edge_id: end.arc_edge_id,
                forward: true,
                pcurve: end.arc_blend_pcurve.clone(),
            }]),
            EndPlan::Cap(end) => Ok(vec![CoedgeRecord {
                id: take_id(),
                edge_id: end.arc_edge_id,
                forward: true,
                pcurve: end.arc_blend_pcurve.clone(),
            }]),
            EndPlan::Miter(end) => end
                .edges
                .iter()
                .map(|(edge_id, forward, pcurve)| {
                    Ok(CoedgeRecord {
                        id: take_id(),
                        edge_id: *edge_id,
                        forward: *forward,
                        pcurve: pcurve.clone(),
                    })
                })
                .collect(),
            EndPlan::Free(free) => {
                let edge_id =
                    free_edge_id.ok_or(KernelRefusal::internal(KernelStage::Sew, "transverse_edge", "blend: free end without its transverse edge"))?;
                Ok(vec![if natural_forward {
                    CoedgeRecord {
                        id: take_id(),
                        edge_id,
                        forward: true,
                        pcurve: free.transverse_blend_pcurve.clone(),
                    }
                } else {
                    CoedgeRecord {
                        id: take_id(),
                        edge_id,
                        forward: false,
                        pcurve: free.transverse_blend_pcurve.reversed().or_refuse(KernelStage::Refine, "reversed")?,
                    }
                }])
            }
        }
    };
    let start_slot = end_slot(&start_end, transverse_a_id, !blend_cr_forward)?;
    let finish_slot = end_slot(&finish_end, transverse_b_id, blend_cr_forward)?;
    // A collapsed rail has no coedge: the end slots on either side of it meet
    // at its one rim point.
    let coedges = if blend_cr_forward {
        let mut coedges = Vec::new();
        if let Some(cr_edge_id) = cr_edge_id {
            coedges.push(CoedgeRecord {
                id: take_id(),
                edge_id: cr_edge_id,
                forward: cr_forward,
                pcurve: crate::sweep_topology::parameter_line(
                    cr_domain[0],
                    0.0,
                    cr_domain[1],
                    0.0,
                ).or_refuse(KernelStage::Refine, "parameter_line")?,
            });
        }
        coedges.extend(finish_slot);
        if let Some(cs_edge_id) = cs_edge_id {
            coedges.push(CoedgeRecord {
                id: take_id(),
                edge_id: cs_edge_id,
                forward: cs_forward,
                pcurve: cs_blend_pcurve_reversed(&cs_domain)?,
            });
        }
        coedges.extend(start_slot);
        coedges
    } else {
        let mut coedges = Vec::new();
        if let Some(cr_edge_id) = cr_edge_id {
            coedges.push(CoedgeRecord {
                id: take_id(),
                edge_id: cr_edge_id,
                forward: cr_forward,
                pcurve: crate::sweep_topology::parameter_line(
                    cr_domain[1],
                    0.0,
                    cr_domain[0],
                    0.0,
                ).or_refuse(KernelStage::Refine, "parameter_line")?,
            });
        }
        coedges.extend(start_slot);
        if let Some(cs_edge_id) = cs_edge_id {
            coedges.push(CoedgeRecord {
                id: take_id(),
                edge_id: cs_edge_id,
                forward: cs_forward,
                pcurve: crate::sweep_topology::parameter_line(
                    cs_domain[0],
                    1.0,
                    cs_domain[1],
                    1.0,
                ).or_refuse(KernelStage::Refine, "parameter_line")?,
            });
        }
        coedges.extend(finish_slot);
        coedges
    };
    let blend_face_id = take_id();
    let mut blend_face = FaceRecord {
        id: blend_face_id,
        surface: rows.surface,
        same_sense,
        loops: vec![LoopRecord {
            id: loop_id,
            coedges,
        }],
        name: name.map(|value| value.to_string()),
    };
    if rows.exact_extrusion {
        transpose_face(&mut blend_face)?;
    }
    let shell_index = result
        .shells
        .iter()
        .position(|shell| shell.faces.iter().any(|face| face.id == first.face.id))
        .ok_or(KernelRefusal::internal(KernelStage::Sew, "mate_shell", "blend: mate shell lost during surgery"))?;
    // Drop the fully-consumed mate faces (lesson 3): their loops collapsed
    // between the sewn edge and the blend; the consumed boundaries and
    // lesson-7 leftovers are deleted below, the sewn edge lives on shared by
    // the blend face and the surviving neighbour.
    for (mate, sew) in [(first, &sew_first), (second, &sew_second)] {
        if sew.is_some() {
            for shell in &mut result.shells {
                shell.faces.retain(|face| face.id != mate.face.id);
            }
        }
    }
    result.shells[shell_index].faces.push(blend_face);

    result.edges.retain(|candidate| candidate.id != edge.id);
    // Delete the boundary edges a prior fillet's corner had left behind that
    // this blend fully consumed.
    result
        .edges
        .retain(|candidate| !consumed_edges.contains(&candidate.id));
    // Each collapsed rail's two rims are one vertex.
    let mut identified = Vec::new();
    for (collapsed, from, to) in [(cr_collapsed, w1a, w1b), (cs_collapsed, w2a, w2b)] {
        if collapsed && from != to {
            identified.push((from, to));
        }
    }
    // The stopped-end rollover's sides: the rail past the crossing and the
    // cap's leg chain move onto the neighbours, and the end footprint is
    // re-traced on every face it touches.
    let mut cr_edge_id = cr_edge_id;
    let mut cs_edge_id = cs_edge_id;
    if tail.iter().any(Option::is_some) {
        let EndPlan::Cap(cap) = &finish_end else {
            return Err(KernelRefusal::unsupported(KernelStage::Sew, "stopped_end_rollover", "blend stopped-end rollover: the rolled stripe's finish end is not a cap, which is the only end this lane closes on the neighbours"));
        };
        let mut footprint = StoppedEndFootprint::default();
        // The cap plane at the finish vertex: through the sharp vertex, normal
        // the edge's tangent there; the fillet lies on the -tangent side.
        {
            let sharp = result.vertices.iter().find(|vertex| vertex.id == cap.sharp_vertex).map(|vertex| vertex.point)
                .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, "stopped_end_rollover", "blend stopped-end rollover: the sharp vertex vanished"))?;
            let d = edge.curve.derivatives(edge.t1, 1).or_refuse(KernelStage::Refine, "derivatives")?;
            footprint.plane = (sharp, d[1].normalized().or_refuse(KernelStage::Refine, "normalized")?);
            footprint.sharp_vertex = cap.sharp_vertex;
        }
        for (side, splice) in tail.into_iter().enumerate() {
            let Some(splice) = splice else { continue };
            let (mate, rail_edge_id, rim, sewn) = if side == 0 {
                (first, cr_edge_id, w1b, sew_first.is_some())
            } else {
                (second, cs_edge_id, w2b, sew_second.is_some())
            };
            let Some(rail_edge_id) = rail_edge_id.filter(|_| !sewn) else {
                return Err(KernelRefusal::unsupported(KernelStage::Sew, "stopped_end_rollover", "blend stopped-end rollover: a rolled side's rail collapsed or sewed onto an existing edge, which this lane does not split"));
            };
            let part_on_mate = roll_side_footprint(result, take_id, mate, &splice, rail_edge_id, rim, cap.sharp_vertex, &mut footprint)?;
            if side == 0 { cr_edge_id = Some(part_on_mate); } else { cs_edge_id = Some(part_on_mate); }
        }
        retrace_stopped_end_footprint(result, take_id, footprint)?;
    }
    Ok(SewnStripe {
        cr_edge_id,
        cs_edge_id,
        blend_face_id,
        consumed: [sew_first.is_some(), sew_second.is_some()],
        snap,
        identified,
    })
}

/// A stopped-end rollover's footprint on the solid, gathered side by side
/// and re-traced once ([`retrace_stopped_end_footprint`]).
#[derive(Default)]
struct StoppedEndFootprint {
    /// The new boundary of the footprint, per face: (face, edge, from, to,
    /// pcurve from -> to on that face).
    pieces: Vec<(u64, u64, u64, u64, NurbsCurve)>,
    /// The faces whose loops change: the mates and every face a piece lies on.
    faces: Vec<u64>,
    /// The cap plane: (sharp point, unit edge tangent at the vertex). The
    /// fillet lies on the side opposite the tangent.
    plane: (Vec3, Vec3),
    sharp_vertex: u64,
}

/// One rolled side, after the pristine sewing: the rail is split at the
/// crossing vertex (on the crossed tangent edge) and the tangent edge there;
/// the pristine splice's rail far part and first leg piece leave the mate's
/// loop (they belong to the neighbours), and the footprint's new boundary on
/// the neighbours — the far part with its pcurve on the neighbour by
/// projection, and the cap's leg chain — is recorded for the re-trace.
/// Returns the rail part left on the mate.
#[allow(clippy::too_many_arguments)]
fn roll_side_footprint(
    result: &mut BrepSolid,
    take_id: &mut dyn FnMut() -> u64,
    mate: &BlendMate,
    splice: &crate::blend::rollover::TailSplice<'_>,
    rail_edge_id: u64,
    rim_vertex: u64,
    sharp_vertex: u64,
    footprint: &mut StoppedEndFootprint,
) -> Result<u64, KernelRefusal> {
    let what = "stopped_end_rollover";
    let not_built = |message: String| KernelRefusal::unsupported(KernelStage::Sew, what, format!("blend stopped-end rollover: {message}"));
    let _ = sharp_vertex;
    let rolled = splice.rolled;
    let crossing_vertex = take_id();
    result.vertices.push(VertexRecord { id: crossing_vertex, point: rolled.point });
    let rail_parts = crate::blend::rollover_surgery::split_edge_at_vertices(result, rail_edge_id, &[(crossing_vertex, rolled.point)], take_id)?;
    let [rail_near, rail_far] = [rail_parts[0], rail_parts[1]];
    let rail_far_edge = result.edges.iter().find(|edge| edge.id == rail_far).cloned()
        .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, "blend stopped-end rollover: the rail's far part vanished"))?;
    if rail_far_edge.end_vertex_id != rim_vertex {
        return Err(not_built(format!("the rail's far part ends at vertex {} rather than the finish rim {rim_vertex}", rail_far_edge.end_vertex_id)));
    }
    // The crossed tangent edge, or the part of it the cap's leg chain left
    // holding the crossing (the chain may have split it already).
    let holds_crossing = |result: &BrepSolid, id: u64| -> Result<bool, KernelRefusal> {
        let Some(edge) = result.edges.iter().find(|edge| edge.id == id) else { return Ok(false) };
        let foot = crate::project_point_to_curve(&edge.curve, rolled.point).or_refuse(KernelStage::Refine, "project_point_to_curve")?;
        Ok(foot.distance <= 1e-6 && foot.u > edge.t0.min(edge.t1) && foot.u < edge.t0.max(edge.t1))
    };
    let crossed = if result.edges.iter().any(|edge| edge.id == rolled.edge_id) {
        rolled.edge_id
    } else {
        let candidates: Vec<u64> = result.edges.iter().map(|edge| edge.id).collect();
        let mut found = None;
        for id in candidates {
            if holds_crossing(result, id)? {
                found = Some(id);
                break;
            }
        }
        found.ok_or_else(|| not_built(format!("no part of tangent edge {} holds the crossing any more", rolled.edge_id)))?
    };
    crate::blend::rollover_surgery::split_edge_at_vertices(result, crossed, &[(crossing_vertex, rolled.point)], take_id)?;
    // The neighbour's pcurve of the rail's far part, by projection.
    let neighbour_surface = result
        .shells
        .iter()
        .flat_map(|shell| &shell.faces)
        .find(|face| face.id == rolled.neighbour_face_id)
        .map(|face| face.surface.clone())
        .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, "blend stopped-end rollover: the neighbour face vanished"))?;
    let far_pcurve = {
        const SAMPLES: usize = 24;
        let [t0, t1] = [rail_far_edge.t0, rail_far_edge.t1];
        let mut uvs = Vec::with_capacity(SAMPLES + 1);
        let mut ts = Vec::with_capacity(SAMPLES + 1);
        for k in 0..=SAMPLES {
            let t = t0 + (t1 - t0) * k as f64 / SAMPLES as f64;
            let point = rail_far_edge.curve.evaluate(t).or_refuse(KernelStage::Refine, "evaluate")?;
            let projection = crate::project_point_to_surface(&neighbour_surface, point).or_refuse(KernelStage::Refine, "project_point_to_surface")?;
            uvs.push(Vec3::new(projection.u, projection.v, 0.0));
            ts.push(t);
        }
        // Clamped over the rail part's own parameter interval (the edge's).
        crate::fit::interpolate_curve(&uvs, 3, &ts).or_refuse(KernelStage::Refine, "interpolate_curve")?
    };
    // The pristine splice's artefacts leave the mate's loop.
    let leg_edge_id = splice.legs.first().map(|piece| piece.edge_id)
        .ok_or_else(|| not_built("a rolled side has no cap leg".to_string()))?;
    let mate_face = result
        .shells
        .iter_mut()
        .flat_map(|shell| shell.faces.iter_mut())
        .find(|face| face.id == mate.face.id)
        .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, "blend stopped-end rollover: the mate face vanished"))?;
    mate_face.loops[mate.loop_index].coedges.retain(|coedge| coedge.edge_id != rail_far && coedge.edge_id != leg_edge_id);
    footprint.pieces.push((rolled.neighbour_face_id, rail_far, crossing_vertex, rim_vertex, far_pcurve));
    for piece in &splice.legs {
        footprint.pieces.push((piece.face_id, piece.edge_id, piece.from, piece.to, piece.pcurve.clone()));
    }
    for face_id in [mate.face.id, rolled.neighbour_face_id].into_iter().chain(splice.legs.iter().map(|piece| piece.face_id)) {
        if !footprint.faces.contains(&face_id) {
            footprint.faces.push(face_id);
        }
    }
    Ok(rail_near)
}

/// Re-trace the footprint: an existing edge of an affected face whose both
/// ends lie on the footprint's new boundary and whose midpoint lies on the
/// fillet's side of the cap plane is INTERIOR to the footprint and deleted
/// (F10: edge 46 between the crossing and the cap leg's exit, edge 68
/// between its crossing and vertex 34); then every affected loop is traced
/// afresh through its remaining coedges and the new pieces — each vertex
/// of the new boundary has exactly one way on — the first closed trace
/// keeping the loop, every further one its own face on the same carrier
/// (F10: face 13's triangle beyond the cap plane).
fn retrace_stopped_end_footprint(
    result: &mut BrepSolid,
    take_id: &mut dyn FnMut() -> u64,
    footprint: StoppedEndFootprint,
) -> Result<(), KernelRefusal> {
    let what = "stopped_end_rollover";
    let not_built = |message: String| KernelRefusal::unsupported(KernelStage::Sew, what, format!("blend stopped-end rollover: {message}"));
    let (sharp_point, tangent) = footprint.plane;
    let mut boundary_vertices: Vec<u64> = vec![footprint.sharp_vertex];
    for (_, _, from, to, _) in &footprint.pieces {
        for vertex in [*from, *to] {
            if !boundary_vertices.contains(&vertex) {
                boundary_vertices.push(vertex);
            }
        }
    }
    let piece_edges: Vec<u64> = footprint.pieces.iter().map(|piece| piece.1).collect();
    // Interior edges, among those on the affected faces.
    let mut interior: Vec<u64> = Vec::new();
    for face in result.shells.iter().flat_map(|shell| &shell.faces).filter(|face| footprint.faces.contains(&face.id)) {
        for coedge in face.loops.iter().flat_map(|lp| &lp.coedges) {
            let Some(edge) = result.edges.iter().find(|edge| edge.id == coedge.edge_id) else { continue };
            if piece_edges.contains(&edge.id) || interior.contains(&edge.id) {
                continue;
            }
            if !(boundary_vertices.contains(&edge.start_vertex_id) && boundary_vertices.contains(&edge.end_vertex_id)) {
                continue;
            }
            let mid = edge.curve.evaluate(0.5 * (edge.t0 + edge.t1)).or_refuse(KernelStage::Refine, "evaluate")?;
            if mid.sub(sharp_point).dot(tangent) < 0.0 {
                interior.push(edge.id);
            }
        }
    }
    if std::env::var("BREP_DEBUG_NETWORK").is_ok() {
        eprintln!("stopped-end rollover: footprint faces {:?}, interior edges {:?}, boundary vertices {:?}", footprint.faces, interior, boundary_vertices);
    }
    let edge_ends: std::collections::HashMap<u64, (u64, u64)> = result.edges.iter().map(|edge| (edge.id, (edge.start_vertex_id, edge.end_vertex_id))).collect();
    let mut new_faces: Vec<FaceRecord> = Vec::new();
    // Face names the result uses (the app keys faces by name; this model
    // already carries `_1` splits of its own): a remnant takes a free suffix.
    let mut taken: Vec<String> = result.shells.iter().flat_map(|shell| &shell.faces).filter_map(|face| face.name.clone()).collect();
    for face_id in footprint.faces.clone() {
        let face = result
            .shells
            .iter_mut()
            .flat_map(|shell| shell.faces.iter_mut())
            .find(|face| face.id == face_id)
            .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, format!("blend stopped-end rollover: face {face_id} vanished")))?;
        let pieces: Vec<&(u64, u64, u64, u64, NurbsCurve)> = footprint.pieces.iter().filter(|piece| piece.0 == face_id).collect();
        let mut rebuilt_loops: Vec<LoopRecord> = Vec::new();
        for loop_record in &face.loops {
            let touched = loop_record.coedges.iter().any(|coedge| interior.contains(&coedge.edge_id))
                || loop_record.coedges.iter().any(|coedge| {
                    edge_ends.get(&coedge.edge_id).is_some_and(|(a, b)| boundary_vertices.contains(a) || boundary_vertices.contains(b))
                });
            if !touched {
                rebuilt_loops.push(loop_record.clone());
                continue;
            }
            // Directed remaining coedges (direction fixed, with their place in
            // the old loop) and undirected pieces.
            let n = loop_record.coedges.len();
            let mut remaining: Vec<(CoedgeRecord, u64, u64, usize)> = Vec::new();
            for (index, coedge) in loop_record.coedges.iter().enumerate() {
                if interior.contains(&coedge.edge_id) {
                    continue;
                }
                let (start, end) = edge_ends.get(&coedge.edge_id).copied()
                    .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, format!("blend stopped-end rollover: coedge {} names a missing edge {}", coedge.id, coedge.edge_id)))?;
                let (from, to) = if coedge.forward { (start, end) } else { (end, start) };
                remaining.push((coedge.clone(), from, to, index));
            }
            let mut loose: Vec<&(u64, u64, u64, u64, NurbsCurve)> = pieces.clone();
            // Trace closed walks: start at a remaining coedge, follow the
            // continuation at each vertex — the old loop's own successor when
            // it is there (a periodic face's loop passes its seam vertex
            // twice), else the unique way on.
            while !remaining.is_empty() {
                // Start on a coedge that is not a degenerate self-loop (a
                // periodic face's pole edge runs from a vertex to itself).
                let start_at = remaining.iter().position(|(_, from, to, _)| from != to).unwrap_or(0);
                let (first, from0, to0, index0) = remaining.remove(start_at);
                let mut walk: Vec<CoedgeRecord> = vec![first];
                let mut at = to0;
                let mut previous = index0;
                let mut guard = 0usize;
                loop {
                    guard += 1;
                    if guard > 4096 {
                        return Err(not_built(format!("face {face_id}: the re-trace does not close")));
                    }
                    let mut next_remaining: Vec<usize> = remaining.iter().enumerate().filter(|(_, (_, from, _, _))| *from == at).map(|(k, _)| k).collect();
                    let successor_is_start = (previous + 1) % n == index0;
                    if at == from0 && (successor_is_start || next_remaining.is_empty()) {
                        // Back at the start along the old loop's own order (a
                        // seam vertex passed twice does not close it early).
                        if at == from0 && successor_is_start {
                            break;
                        }
                        if loose.iter().all(|piece| piece.2 != at && piece.3 != at) {
                            break;
                        }
                    }
                    if next_remaining.len() > 1 {
                        if let Some(k) = next_remaining.iter().copied().find(|k| remaining[*k].3 == (previous + 1) % n) {
                            next_remaining = vec![k];
                        }
                    }
                    let next_pieces: Vec<usize> = loose.iter().enumerate().filter(|(_, piece)| piece.2 == at || piece.3 == at).map(|(k, _)| k).collect();
                    match (next_remaining.len(), next_pieces.len()) {
                        (1, 0) => {
                            let (coedge, _, to, index) = remaining.remove(next_remaining[0]);
                            walk.push(coedge);
                            at = to;
                            previous = index;
                        }
                        (0, 1) => {
                            let piece = loose.remove(next_pieces[0]);
                            let (edge_id, from, to, pcurve) = (piece.1, piece.2, piece.3, &piece.4);
                            let (start, _) = edge_ends.get(&edge_id).copied()
                                .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, format!("blend stopped-end rollover: footprint edge {edge_id} missing")))?;
                            let along = from == at;
                            let coedge_forward = if along { start == from } else { start == to };
                            walk.push(CoedgeRecord {
                                id: take_id(),
                                edge_id,
                                forward: coedge_forward,
                                pcurve: if along { pcurve.clone() } else { pcurve.reversed().or_refuse(KernelStage::Refine, "reversed")? },
                            });
                            at = if along { to } else { from };
                        }
                        (r, p) => {
                            return Err(not_built(format!("face {face_id}: vertex {at} has {} ways on ({r} boundary, {p} new), not one", r + p)));
                        }
                    }
                }
                rebuilt_loops.push(LoopRecord { id: if rebuilt_loops.is_empty() { loop_record.id } else { take_id() }, coedges: walk });
            }
            if !loose.is_empty() {
                return Err(not_built(format!("face {face_id}: {} new boundary piece(s) joined no loop", loose.len())));
            }
        }
        // The first loop keeps the face; every further closed trace of a
        // touched loop is a face of its own on the same carrier.
        let kept = rebuilt_loops.remove(0);
        face.loops = vec![kept];
        let surface = face.surface.clone();
        let same_sense = face.same_sense;
        let base_name = face.name.clone();
        for extra in rebuilt_loops {
            let name = base_name.as_ref().map(|base| {
                (1..).map(|k| format!("{base}_{k}")).find(|candidate| !taken.contains(candidate)).expect("a free name")
            });
            if let Some(name) = &name {
                taken.push(name.clone());
            }
            new_faces.push(FaceRecord { id: take_id(), surface: surface.clone(), same_sense, loops: vec![extra], name });
        }
    }
    if !new_faces.is_empty() {
        let shell_index = result.shells.iter().position(|shell| !shell.faces.is_empty())
            .ok_or_else(|| KernelRefusal::internal(KernelStage::Sew, what, "blend stopped-end rollover: no shell"))?;
        result.shells[shell_index].faces.extend(new_faces);
    }
    result.edges.retain(|edge| !interior.contains(&edge.id));
    Ok(())
}

/// Swap the two parameters of a face: the surface's control net, every
/// coedge pcurve, and the sense (S_u × S_v changes sign).  The face's
/// geometry is untouched; only its chart is.  Used to hand an exact
/// cylinder patch built with its section along v to the analytic recogniser,
/// which wants the circle along u.
pub(in crate::blend) fn transpose_face(face: &mut FaceRecord) -> Result<(), KernelRefusal> {
    let surface = &face.surface;
    let rows_u = surface.control_points.len();
    let rows_v = surface.control_points.first().map(|row| row.len()).unwrap_or(0);
    let mut transposed = vec![Vec::with_capacity(rows_u); rows_v];
    for row in &surface.control_points {
        for (j, point) in row.iter().enumerate() {
            transposed[j].push(*point);
        }
    }
    face.surface = NurbsSurface::new(
        surface.degree_v,
        surface.degree_u,
        surface.knots_v.clone(),
        surface.knots_u.clone(),
        transposed,
    ).or_refuse(KernelStage::Refine, "new")?;
    for loop_record in &mut face.loops {
        for coedge in &mut loop_record.coedges {
            for control in &mut coedge.pcurve.control_points {
                std::mem::swap(&mut control.x, &mut control.y);
            }
        }
    }
    face.same_sense = !face.same_sense;
    Ok(())
}

/// Drop every vertex no longer referenced by an edge — the sharp corners the
/// blends replaced, and anything freed by a consumed boundary.  Run ONCE after
/// a whole group of stripes is in: a corner vertex is still referenced by the
/// selected edges that have not been sewn yet.
pub(in crate::blend) fn prune_orphan_vertices(result: &mut BrepSolid) {
    let used: rustc_hash::FxHashSet<u64> = result
        .edges
        .iter()
        .flat_map(|candidate| [candidate.start_vertex_id, candidate.end_vertex_id])
        .collect();
    result
        .vertices
        .retain(|candidate| used.contains(&candidate.id));
}

fn cs_blend_pcurve_reversed(cs_domain: &[f64; 2]) -> Result<NurbsCurve, KernelRefusal> {
    crate::sweep_topology::parameter_line(cs_domain[1], 1.0, cs_domain[0], 1.0).or_refuse(KernelStage::Refine, "parameter_line")
}