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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ the reproducer). What a bridge author needs without opening it:

- `BRepExtrema_ExtCC` crashes on parallel edges: `if (result.isParallel) { return result; }`
before reading points. `Extrema_ExtCC::Points` itself over-reads on the same input (patch `0024`).
- `LocOpe_SplitDrafts::Perform()` throws on incompatible geometry: always `try`/`catch` it.
- `OCC_CATCH_SIGNALS` is inert in this build (no `OCC_CONVERT_SIGNALS`). An OS signal raised
inside OCCT is uncatchable in-process, and so is a C++ exception that reaches the Swift boundary
(#345), which is why every `gp_Dir`/`gp_Ax*`/`Geom_Direction` construction from caller doubles
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

A comprehensive Swift wrapper for [OpenCASCADE Technology (OCCT)](https://www.opencascade.com/) 8.0.1, providing B-Rep solid modeling for macOS and iOS. **v3.0.0. SemVer-stable; see [SEMVER.md](docs/SEMVER.md#v300) before upgrading from v2.x.**

**4,366 wrapped operations** | macOS 12+ / iOS 15+ (arm64), visionOS and tvOS untested | OCCT 8.0.1
**4,365 wrapped operations** | macOS 12+ / iOS 15+ (arm64), visionOS and tvOS untested | OCCT 8.0.1
## Quick Start

### Installation
Expand Down
36 changes: 35 additions & 1 deletion Scripts/repro/1393-splitdrafts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,25 @@ the process**. Through the bridge it is catchable (the Swift call returns `nil`,
regression test asserts), so this is a difference in where the throw crosses, not in whether it
happens.

## Disposition
## Outcome: the wrapper is gone

`Shape.splitDrafts` was removed in v4.0.0 rather than repaired, and no patch is carried.

The deciding fact is not the defect, it is upstream's own verdict on the class. OCCT deleted
`LocOpe_SplitDrafts` outright on 2026-08-07 in
[OCCT#1442](https://github.com/Open-Cascade-SAS/OCCT/pull/1442), a "clean up dead headers" pass, and
`git grep SplitDrafts upstream/master` returns nothing. It had no caller anywhere in the OCCT tree,
not even a DRAW command, which is also how a defect this total survived to 8.0.1 unnoticed.

So there was no upstream PR to open, nothing to fix for anyone else's benefit, and a carried patch
would have been this project reviving a class its own maintainer had just removed, with a guaranteed
expiry at the first kernel bump past that commit. `okf/policies/scope-boundary.md` says stay
faithful to OCCT; wrapping what OCCT has deleted is the opposite of that.

The record below is kept because the investigation is what made the decision possible, and because
the next reader who wonders why a 1996 class is missing deserves the answer.

## Disposition, as it stood before the removal

- **Test**: `Tests/OCCTModelingTests/Issue1393SplitDraftsTests.swift` asserts the refusal, and says
in its own comment that a non-nil result means a repin fixed the kernel and the test should then
Expand All @@ -64,3 +82,19 @@ happens.
- **Not done**: no bridge-side guard. Refusing the call up front would be refusing the whole
operation, and the `nil` it already returns is the same answer with the kernel's own reason
behind it.

## What is kept from the fix that was not taken

`upstream/LocOpe_SplitDrafts_Test.cxx` is the GTest written for an upstream PR that could not be
filed, since the class it tests no longer exists upstream. It is kept for one reason: it is the
only executable statement of what a working `LocOpe_SplitDrafts` should produce, proven both ways
against a patched and an unpatched kernel (`IsDone=1`, seven faces, one of them tilted by exactly
the requested draft angle, a 10-unit cube's volume rising 1000 to 1022.04 against the wedge's
analytic 22.04).

If OCCT ever restores the class, or a downstream fork keeps it, that file is the head start. If
nobody ever does, it is the record of what was measured before the wrapper was removed, which is
worth more than the diff that was discarded with it.

The patch itself, `0034`, was never carried. It exists only in the closed PR
[#1627](https://github.com/SecondMouseAU/OCCTSwift/pull/1627).
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2026 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepGProp.hxx>
#include <BRepLib.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <Geom2d_Line.hxx>
#include <Geom_Plane.hxx>
#include <GProp_GProps.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <Precision.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pln.hxx>
#include <gp_Pnt2d.hxx>

#include <gtest/gtest.h>

namespace
{
//! The +Z face of a box built at the origin.
TopoDS_Face TopFace(const TopoDS_Shape& theBox, const double theHeight)
{
for (TopExp_Explorer anExp(theBox, TopAbs_FACE); anExp.More(); anExp.Next())
{
const TopoDS_Face aFace = TopoDS::Face(anExp.Current());
const occ::handle<Geom_Plane> aPlane = occ::down_cast<Geom_Plane>(BRep_Tool::Surface(aFace));
if (!aPlane.IsNull()
&& std::abs(aPlane->Pln().Location().Z() - theHeight) < Precision::Confusion())
{
return aFace;
}
}
return TopoDS_Face();
}
} // namespace

// Perform() pipes along the intersection of the two drafted planes, which is always a line, and
// the pipe's path is the face normal, also a line. Both were passed to GeomFill_Pipe untrimmed,
// and GeomConvert::CurveToBSplineCurve rejects an infinite curve, so every planar request threw
// Standard_DomainError("No such curve").
TEST(LocOpe_SplitDraftsTest, DraftPlanarFace)
{
const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape();
const TopoDS_Face aTop = TopFace(aBox, 10.0);
ASSERT_FALSE(aTop.IsNull()) << "Top face not found";

// The splitting wire, built on the face at u = 5 so that it carries a pcurve.
const occ::handle<Geom_Surface> aSurf = BRep_Tool::Surface(aTop);
const occ::handle<Geom2d_Line> aLine2d = new Geom2d_Line(gp_Pnt2d(5.0, 0.0), gp_Dir2d(0.0, 1.0));
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(aLine2d, aSurf, 0.0, 10.0).Edge();
BRepLib::BuildCurves3d(anEdge);
const TopoDS_Wire aWire = BRepBuilderAPI_MakeWire(anEdge).Wire();

// The neutral plane must not be the face's own plane, or NewPlane() bails.
const gp_Pln aNeutral(gp_Pnt(5.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
const double anAngle = 10.0 * M_PI / 180.0;

LocOpe_SplitDrafts aSplit;
aSplit.Init(aBox);
ASSERT_NO_THROW(aSplit.Perform(aTop, aWire, gp_Dir(1.0, 0.0, 0.0), aNeutral, anAngle));
ASSERT_TRUE(aSplit.IsDone()) << "Draft split not performed";

int aNbFaces = 0;
for (TopExp_Explorer anExp(aSplit.Shape(), TopAbs_FACE); anExp.More(); anExp.Next())
{
++aNbFaces;
}
EXPECT_EQ(aNbFaces, 7) << "The top face should be split in two";

// Half the top face (5 x 10) is tipped up by the draft angle, adding that wedge.
GProp_GProps aProps;
BRepGProp::VolumeProperties(aSplit.Shape(), aProps);
const double anExpected = 1000.0 + 0.5 * 5.0 * (5.0 * tan(anAngle)) * 10.0;
EXPECT_NEAR(aProps.Mass(), anExpected, 1.0e-4) << "Drafted volume is wrong";
}
1 change: 0 additions & 1 deletion Sources/OCCTBridge/include/OCCTBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,6 @@
// LocOpe_Prism → OCCTLocOpePrism
// LocOpe_Revol → OCCTLocOpeRevol
// LocOpe_RevolutionForm → OCCTLocOpeRevolutionForm
// LocOpe_SplitDrafts → OCCTLocOpeSplitDrafts
// LocOpe_SplitShape → OCCTLocOpeSplitShape*
// LocOpe_Spliter → OCCTLocOpeSplitByWire*
// LocOpe_WiresOnShape → OCCTLocOpeBuildWires, OCCTLocOpeSplitByWire*
Expand Down
25 changes: 0 additions & 25 deletions Sources/OCCTBridge/include/OCCTBridge_Modeling.h
Original file line number Diff line number Diff line change
Expand Up @@ -1906,31 +1906,6 @@ OCCTShapeRef _Nullable OCCTLocOpeSplitShapeByVertex(OCCTShapeRef shape,
int32_t edgeIndex,
double parameter);

// --- LocOpe_SplitDrafts ---

/// Split a face with draft angles on both sides of a wire.
/// @param shape Shape containing the face
/// @param faceIndex Index of the face to split
/// @param wire Wire defining the split
/// @param dirX,dirY,dirZ Extraction direction
/// @param planeOriginX,Y,Z Neutral plane origin
/// @param planeNormalX,Y,Z Neutral plane normal
/// @param angle Draft angle in radians
/// @return Modified shape, or NULL on failure
OCCTShapeRef _Nullable OCCTLocOpeSplitDrafts(OCCTShapeRef shape,
int32_t faceIndex,
OCCTShapeRef wire,
double dirX,
double dirY,
double dirZ,
double planeOriginX,
double planeOriginY,
double planeOriginZ,
double planeNormalX,
double planeNormalY,
double planeNormalZ,
double angle);

// --- LocOpe_FindEdges ---

// #613: both finders return a SELECTION of edges, not an enumeration, so the position of an entry
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ void occtEnsureSignals()
#include <LocOpe_LinearForm.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitShape.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_FindEdges.hxx>
#include <LocOpe_FindEdgesInFace.hxx>
#include <LocOpe_CSIntersector.hxx>
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge_Modeling_Boolean.mm
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge_Modeling_Chamfer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
62 changes: 0 additions & 62 deletions Sources/OCCTBridge/src/OCCTBridge_Modeling_Features.mm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down Expand Up @@ -1710,67 +1709,6 @@ OCCTShapeRef OCCTLocOpeSplitShapeByVertex(OCCTShapeRef shape, int32_t edgeIndex,
}
}

OCCTShapeRef OCCTLocOpeSplitDrafts(OCCTShapeRef shape,
int32_t faceIndex,
OCCTShapeRef wire,
double dirX,
double dirY,
double dirZ,
double planeOriginX,
double planeOriginY,
double planeOriginZ,
double planeNormalX,
double planeNormalY,
double planeNormalZ,
double angle)
{
// #1026: the wire's ShapeType() read below is an unguarded myTShape dereference.
if (!occtShapeIsPresent(shape) || !occtShapeIsPresent(wire))
return nullptr;
try
{
LocOpe_SplitDrafts splitDrafts;
splitDrafts.Init(shape->shape);

// #541: the shared face enumeration, so this names the face face(at:) names.
TopoDS_Face face = occtFaceAt(shape->shape, faceIndex);
if (face.IsNull())
return nullptr;

// Extract wire
TopoDS_Wire w;
if (wire->shape.ShapeType() == TopAbs_WIRE)
{
w = TopoDS::Wire(wire->shape);
}
else
{
for (TopExp_Explorer exp(wire->shape, TopAbs_WIRE); exp.More(); exp.Next())
{
w = TopoDS::Wire(exp.Current());
break;
}
}
if (w.IsNull())
return nullptr;

gp_Dir dir(dirX, dirY, dirZ);
gp_Pln plane(gp_Pnt(planeOriginX, planeOriginY, planeOriginZ),
gp_Dir(planeNormalX, planeNormalY, planeNormalZ));

splitDrafts.Perform(face, w, dir, plane, angle);

TopoDS_Shape result = splitDrafts.Shape();
if (result.IsNull())
return nullptr;
return new OCCTShape(result);
}
catch (...)
{
return nullptr;
}
}

// #613: a finder returns a SELECTION, so the position of an entry in outEdges is a result slot, not
// an index into anything. Swift wrote that slot number into Edge.index all the same. Measured on a
// 10mm box (identically for the origin-centred and origin-at-zero spellings), edgesInFace(at: 3)
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge_Modeling_Fillet.mm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge_Modeling_Misc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge_Modeling_Sweep.mm
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
1 change: 0 additions & 1 deletion Sources/OCCTBridge/src/OCCTBridge_Modeling_Transform.mm
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <LocOpe_Prism.hxx>
#include <LocOpe_Revol.hxx>
#include <LocOpe_RevolutionForm.hxx>
#include <LocOpe_SplitDrafts.hxx>
#include <LocOpe_SplitShape.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeWire.hxx>
Expand Down
Loading