Conversation
…_Pipe
Shape.splitDrafts could not succeed for any input. LocOpe_SplitDrafts::Perform
sweeps the plane/plane intersection (always an infinite Geom_Line) along the
face normal (another one), and GeomFill_Pipe converts both through
GeomConvert::CurveToBSplineCurve, which refuses an infinite curve by documented
design, so every valid call threw Standard_DomainError("No such curve").
The missing untrimmed Geom_Line case in CurveToBSplineCurve is not the bug: its
trimmed branch has always converted a line exactly, and the caller is what hands
an infinite one to an API that says it rejects them. Scripts/patches/0034 trims
both, and both again at the second pipe site, to the shape's bounding-box extent.
Not upstream-bound: OCCT master deleted LocOpe_SplitDrafts in OCCT#1442 as dead
code, so the patch retires by deletion at the next kernel bump past that tag.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Closing unmerged. The decision went the other way, and this PR is the artefact of the option not taken: The reasoning is upstream's rather than ours. None of the work here is wasted, and two parts of it are why the decision could be made at all. The investigation that reached the real mechanism, The GTest written for a PR that could never be filed is the one genuine loss. It is recorded in the reproducer directory rather than deleted. |
What & why
Shape.splitDraftshas never once produced a result. Every call that gets past the planar-facecheck throws
Standard_DomainError("No such curve"), so a shipped, documented, public API returnsnilfor every input.Scripts/patches/0034fixes the kernel, and the regression test becomes abehavioural one.
The brief this started from, and #1393's own writeup, had the fix in the wrong place, and the
correction is the substance of this PR. Both concluded that
GeomConvert::CurveToBSplineCurve"has no
Geom_Linecase" and that adding one was the fix. It has two branches, not one chain:Geom_TrimmedCurvebranch does handleGeom_Line, atGeomConvert.cxx:190, and alwayshas. Verified directly: a
Geom_TrimmedCurve(line, -50, 50)converts to degree 1 with poles atthe two trim points.
GeomConvert.hxxsays above the declaration: "Raises DomainError if the curve C is infinite".Geom_Lineis infinite. There is no parameter range to convert over, so there is nothing correctto add there, and adding one would have produced poles at ±1e100.
The defect is in the caller.
LocOpe_SplitDrafts::Performsweeps the plane/plane intersection(always a
Geom_Line) alongnew Geom_Line(NormalFg), and hands both, untrimmed, toGeomFill_Pipe, whoseGeomFill_UniformSectionconstructor callsCurveToBSplineCurve. It makesthe same mistake a second time further down
Perform(), on a wire edge's basis curve it has justdeliberately stripped of its
Geom_TrimmedCurvewrapper. The patch adds a file-localTrimInfinite()that leaves a finite curve alone and trims an infinite one, from its own origin,to the diagonal of the shape's bounding box enlarged by that origin, and applies it at all three
sites. No new toolkit dependency:
TKTopAlgois already inTKFeat'sEXTERNLIB.cmake.Closes #1393
CHANGELOG entry
Shape.splitDraftsworks, on a kernel carrying the new patch0034(#1393)Shape.splitDraftsreturnednilfor every input on OCCT 8.0.1, so the operation had neversucceeded once since it was wrapped.
LocOpe_SplitDrafts::Performsweeps the intersection of thetwo drafted planes (always an infinite
Geom_Line) along the face normal (another infiniteGeom_Line), andGeomFill_Pipeconverts both throughGeomConvert::CurveToBSplineCurve, whichrefuses an infinite curve by documented design. Every valid call therefore threw
Standard_DomainError("No such curve"), which the bridge'scatch (...)turned intonil.Scripts/patches/0034trims both lines, and a third at
Perform()'s second pipe site, to the shape's own bounding-boxextent before the pipe sees them. On a 10 mm box, drafting half the top face by 10 degrees:
The patch is not in
Package.swift's pinned kernel asset, soShape.splitDraftsstill returnsnilfor consumers of the released package until a repin. It is also the one carried patch thatwill never be retired by a repin: OCCT master deleted
LocOpe_SplitDraftsoutright inOCCT#1442 as dead code, so there is no
upstream fix to wait for, and the first kernel bump past that tag removes the class and this
wrapper with it.
SemVer impact
PATCH. No API surface changes. A consumer sees
Shape.splitDraftsreturn a drafted solid insteadof
nil, but only against a kernel built fromScripts/patches/; against the pinned asset thereleased behaviour is unchanged. No migration.
Checklist
verification), see SecondMouseAU/OCCTReconstruct#397
for the ecosystem-wide test-coverage standard this is piloting.
--self-testcase was run once with its subject broken, and thefailure is reported here, see okf/policies/prove-the-test-fails.md.
docs/CHANGELOG.mdis not in this diff.docs/SEMVER.mdis not in this diff.It is assessed at release on
main, not per PR.Notes for the reviewer
How CI will behave, deliberately
Tests/OCCTModelingTests/Issue1393SplitDraftsTests.swiftnow holds one test per kernel, gatedon
OCCTSWIFT_LOCAL, which is the convention0027's test set (PR #915 review, finding 1):planarRequestIsRefusedOnPinnedKernel, enabled whenOCCTSWIFT_LOCAL != "1", asserts therefusal. This is what
ci.yml'sbuild-and-testruns, and it passes, because the pinned assethas no
0034. CI is green, not red.planarRequestDraftsTheFace, enabled whenOCCTSWIFT_LOCAL == "1", asserts the drafted result.kernel-integration.yml(triggered byScripts/patches/**, buildsV8_0_1plus every carriedpatch from source, runs
OCCTSWIFT_LOCAL=1 swift test) is the job that exercises it.The refusal assertion is not deleted with nothing measuring the new behaviour, and neither kernel
is left untested. When a repin eventually carries
0034, the refusal test fails loudly on thepinned side, which is the signal to delete it.
Verification, and proving both tests fail
No full kernel rebuild. Override-link for the C++ level, and an
arsplice of the single patchedtranslation unit into the local
libOCCT-macos.afor the Swift level (restored byte-identicalafterwards,
cmpclean).C++,
Scripts/repro/1393-splitdrafts/probe.mm. Pristine translation unit(
git show HEAD:<path>), same link line:Patched translation unit:
The result is geometrically right, not merely non-throwing. Exactly one face is tilted, its normal
(0.174, 0.000, 0.985), which is 10.000 degrees off vertical, and the volume goes 1000 to 1022.04against the 22.04 the drafted wedge adds (
0.5 * 5 * 5*tan(10 deg) * 10), agreeing to five figures.Swift, both directions (
okf/policies/prove-the-test-fails.md):OCCTSWIFT_LOCAL1planarRequestDraftsTheFacepassed, refusal test skipped1planarRequestDraftsTheFacefailed:Expectation failed: maybeResult → nilplanarRequestIsRefusedOnPinnedKernelpassed, behavioural test skippedThe middle row is the proof the behavioural test measures the fix rather than the absence of a
throw.
No upstream PR, and why not
okf/policies/upstream-occt-patch-process.mdsays go straight to a PR with a GTest. There isnothing to open one against. OCCT master deleted
LocOpe_SplitDraftson 2026-08-07 inOCCT#1442, "Coding, Modeling Algo - Clean up
dead headers", 236 files and 24858 deletions, by maintainer dpasukhi.
git grep SplitDraftsonupstream/masterreturns nothing; the class had no caller anywhere in the OCCT tree, not even aDRAW command, which is both why it was removed and the most likely reason a defect this total
reached 8.0.1 unnoticed. Opening a PR to restore and fix a class a maintainer just deleted as dead
would be arguing with a decision already taken.
The GTest was written and proven both ways all the same, and is kept rather than discarded, at
Scripts/repro/1393-splitdrafts/upstream/LocOpe_SplitDrafts_Test.cxx. It would have beenTKFeat'sfirst (its
GTests/FILES.cmakeis empty upstream). It fails with the exactStandard_DomainError: "No such curve"against the unpatched TU and passes against the patched one.Follow-on
Shape.splitDraftswraps a class OCCT has removed. That is a decision for a separate issue,not this PR: the next kernel bump past OCCT#1442 makes
Scripts/patches/0034unapplicable and thebridge function uncompilable, so both the wrapper and the patch have to go at that point. Recorded
in
Scripts/patches/README.md,okf/references/carried-occt-patches.md,okf/references/known-occt-bugs.md,docs/reference/Shape-Measurement.mdandPackage.swift'smanifest comment, all of which say "retire by deletion, not by a repin".
Records updated
ls Scripts/patches/*.patch | wc -lnow answers 23, and the three places that state it weremoved together:
CLAUDE.md("twenty-three on disk, seventeen pinned"),okf/references/carried-occt-patches.md(count, its "Pinned against carried" table row) andPackage.swift's manifest comment (count, the enumeration of the unpinned set, and the twoparagraphs that said "the five").
0034is the only one of the six unpinned patches with aSwift-reachable assertion, so the manifest's "it cannot prove any of the five fixes works" sentence
is now correct rather than approximately correct.
Scripts/repro/1393-splitdrafts/README.mdcarries an explicit "Correction: the missingGeom_Linecase is not the bug" section rather than a silent rewrite, since its first reading iswhat the brief for this work inherited.
Gates: all eight plus the four censuses clean.