248 lines
8.1 KiB
C++
248 lines
8.1 KiB
C++
#include <BRepAlgoAPI_Common.hxx>
|
|
#include <BRepAlgoAPI_Cut.hxx>
|
|
#include <BRepAlgoAPI_Fuse.hxx>
|
|
#include <BRepPrimAPI_MakeBox.hxx>
|
|
#include <Standard_Version.hxx>
|
|
#include <STEPControl_Reader.hxx>
|
|
#include <STEPControl_Writer.hxx>
|
|
#include <STEPControl_StepModelType.hxx>
|
|
#include <TopAbs_ShapeEnum.hxx>
|
|
#include <TopExp_Explorer.hxx>
|
|
#include <TopoDS_Shape.hxx>
|
|
|
|
#include <emscripten/bind.h>
|
|
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
struct HistoryRecord
|
|
{
|
|
std::string relation;
|
|
std::string kind;
|
|
int sourceIndex = -1;
|
|
int resultIndex = -1;
|
|
};
|
|
|
|
std::string kindName(const TopAbs_ShapeEnum theKind)
|
|
{
|
|
switch (theKind)
|
|
{
|
|
case TopAbs_VERTEX: return "vertex";
|
|
case TopAbs_EDGE: return "edge";
|
|
case TopAbs_WIRE: return "wire";
|
|
case TopAbs_FACE: return "face";
|
|
case TopAbs_SHELL: return "shell";
|
|
case TopAbs_SOLID: return "solid";
|
|
case TopAbs_COMPSOLID: return "compsolid";
|
|
case TopAbs_COMPOUND: return "compound";
|
|
default: return "shape";
|
|
}
|
|
}
|
|
|
|
std::vector<TopoDS_Shape> subShapes(const TopoDS_Shape& theShape, const TopAbs_ShapeEnum theKind)
|
|
{
|
|
std::vector<TopoDS_Shape> aShapes;
|
|
for (TopExp_Explorer anExplorer(theShape, theKind); anExplorer.More(); anExplorer.Next())
|
|
{
|
|
aShapes.push_back(anExplorer.Current());
|
|
}
|
|
return aShapes;
|
|
}
|
|
|
|
std::string writeStep(const TopoDS_Shape& theShape);
|
|
|
|
int resultIndex(const TopoDS_Shape& theShape,
|
|
const TopoDS_Shape& theResult,
|
|
const TopAbs_ShapeEnum theKind)
|
|
{
|
|
int anIndex = 0;
|
|
for (TopExp_Explorer anExplorer(theResult, theKind); anExplorer.More(); anExplorer.Next(), ++anIndex)
|
|
{
|
|
if (anExplorer.Current().IsSame(theShape))
|
|
{
|
|
return anIndex;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
template <typename TAlgorithm>
|
|
emscripten::val runBoolean(TAlgorithm& theAlgorithm,
|
|
const TopoDS_Shape& theObject,
|
|
const TopoDS_Shape& theTool)
|
|
{
|
|
theAlgorithm.Build();
|
|
if (!theAlgorithm.IsDone() || theAlgorithm.HasErrors())
|
|
{
|
|
throw std::runtime_error("OCCT boolean operation failed while collecting native history.");
|
|
}
|
|
|
|
const TopoDS_Shape aResult = theAlgorithm.Shape();
|
|
emscripten::val aRecords = emscripten::val::array();
|
|
const TopAbs_ShapeEnum aKinds[] = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE};
|
|
const TopoDS_Shape aSources[] = {theObject, theTool};
|
|
const char* aSourceNames[] = {"object", "tool"};
|
|
unsigned int aRecordIndex = 0;
|
|
|
|
for (int aSource = 0; aSource < 2; ++aSource)
|
|
{
|
|
for (const TopAbs_ShapeEnum aKind : aKinds)
|
|
{
|
|
const auto aSubShapes = subShapes(aSources[aSource], aKind);
|
|
for (std::size_t aSourceIndex = 0; aSourceIndex < aSubShapes.size(); ++aSourceIndex)
|
|
{
|
|
const TopoDS_Shape& anInput = aSubShapes[aSourceIndex];
|
|
const auto& aModified = theAlgorithm.Modified(anInput);
|
|
for (NCollection_List<TopoDS_Shape>::Iterator anIterator(aModified); anIterator.More(); anIterator.Next())
|
|
{
|
|
const int anOutputIndex = resultIndex(anIterator.Value(), aResult, aKind);
|
|
if (anOutputIndex < 0) continue;
|
|
emscripten::val aRecord = emscripten::val::object();
|
|
aRecord.set("relation", "modified");
|
|
aRecord.set("source", aSourceNames[aSource]);
|
|
aRecord.set("kind", kindName(aKind));
|
|
aRecord.set("sourceIndex", static_cast<int>(aSourceIndex));
|
|
aRecord.set("resultIndex", anOutputIndex);
|
|
aRecords.set(aRecordIndex++, aRecord);
|
|
}
|
|
const auto& aGenerated = theAlgorithm.Generated(anInput);
|
|
for (NCollection_List<TopoDS_Shape>::Iterator anIterator(aGenerated); anIterator.More(); anIterator.Next())
|
|
{
|
|
const int anOutputIndex = resultIndex(anIterator.Value(), aResult, aKind);
|
|
if (anOutputIndex < 0) continue;
|
|
emscripten::val aRecord = emscripten::val::object();
|
|
aRecord.set("relation", "generated");
|
|
aRecord.set("source", aSourceNames[aSource]);
|
|
aRecord.set("kind", kindName(aKind));
|
|
aRecord.set("sourceIndex", static_cast<int>(aSourceIndex));
|
|
aRecord.set("resultIndex", anOutputIndex);
|
|
aRecords.set(aRecordIndex++, aRecord);
|
|
}
|
|
if (theAlgorithm.IsDeleted(anInput))
|
|
{
|
|
emscripten::val aRecord = emscripten::val::object();
|
|
aRecord.set("relation", "deleted");
|
|
aRecord.set("source", aSourceNames[aSource]);
|
|
aRecord.set("kind", kindName(aKind));
|
|
aRecord.set("sourceIndex", static_cast<int>(aSourceIndex));
|
|
aRecord.set("resultIndex", -1);
|
|
aRecords.set(aRecordIndex++, aRecord);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
emscripten::val aResponse = emscripten::val::object();
|
|
aResponse.set("provider", "occt-native");
|
|
aResponse.set("occtVersion", OCC_VERSION_COMPLETE);
|
|
aResponse.set("result", aResult);
|
|
aResponse.set("resultStep", writeStep(aResult));
|
|
aResponse.set("records", aRecords);
|
|
aResponse.set("hasModified", theAlgorithm.HasModified());
|
|
aResponse.set("hasGenerated", theAlgorithm.HasGenerated());
|
|
aResponse.set("hasDeleted", theAlgorithm.HasDeleted());
|
|
return aResponse;
|
|
}
|
|
|
|
TopoDS_Shape makeBox(const double theX, const double theY, const double theZ)
|
|
{
|
|
if (theX <= 0.0 || theY <= 0.0 || theZ <= 0.0)
|
|
{
|
|
throw std::invalid_argument("Box dimensions must be positive.");
|
|
}
|
|
return BRepPrimAPI_MakeBox(theX, theY, theZ).Shape();
|
|
}
|
|
|
|
std::string occtVersion()
|
|
{
|
|
return OCC_VERSION_COMPLETE;
|
|
}
|
|
|
|
emscripten::val booleanHistory(const TopoDS_Shape& theObject,
|
|
const TopoDS_Shape& theTool,
|
|
const std::string& theOperation)
|
|
{
|
|
if (theOperation == "fuse")
|
|
{
|
|
BRepAlgoAPI_Fuse anAlgorithm(theObject, theTool);
|
|
return runBoolean(anAlgorithm, theObject, theTool);
|
|
}
|
|
if (theOperation == "cut")
|
|
{
|
|
BRepAlgoAPI_Cut anAlgorithm(theObject, theTool);
|
|
return runBoolean(anAlgorithm, theObject, theTool);
|
|
}
|
|
if (theOperation == "common")
|
|
{
|
|
BRepAlgoAPI_Common anAlgorithm(theObject, theTool);
|
|
return runBoolean(anAlgorithm, theObject, theTool);
|
|
}
|
|
throw std::invalid_argument("Unsupported Boolean operation. Expected fuse, cut or common.");
|
|
}
|
|
|
|
TopoDS_Shape readStep(const std::string& theText)
|
|
{
|
|
if (theText.empty())
|
|
{
|
|
throw std::invalid_argument("STEP text must not be empty.");
|
|
}
|
|
std::istringstream aStream(theText);
|
|
STEPControl_Reader aReader;
|
|
const IFSelect_ReturnStatus aStatus = aReader.ReadStream("bitbybit.step", aStream);
|
|
if (aStatus != IFSelect_RetDone || aReader.NbRootsForTransfer() <= 0 || !aReader.TransferRoots())
|
|
{
|
|
throw std::runtime_error("OCCT could not read and transfer the supplied STEP text.");
|
|
}
|
|
const TopoDS_Shape aShape = aReader.OneShape();
|
|
if (aShape.IsNull())
|
|
{
|
|
throw std::runtime_error("OCCT STEP transfer produced a null shape.");
|
|
}
|
|
return aShape;
|
|
}
|
|
|
|
std::string writeStep(const TopoDS_Shape& theShape)
|
|
{
|
|
if (theShape.IsNull())
|
|
{
|
|
throw std::invalid_argument("Cannot serialize a null OCCT shape.");
|
|
}
|
|
STEPControl_Writer aWriter;
|
|
if (aWriter.Transfer(theShape, STEPControl_AsIs) != IFSelect_RetDone)
|
|
{
|
|
throw std::runtime_error("OCCT could not transfer the shape to STEP.");
|
|
}
|
|
std::ostringstream aStream;
|
|
if (aWriter.WriteStream(aStream) != IFSelect_RetDone)
|
|
{
|
|
throw std::runtime_error("OCCT could not write the shape to STEP.");
|
|
}
|
|
return aStream.str();
|
|
}
|
|
|
|
emscripten::val booleanHistoryFromStep(const std::string& theObjectStep,
|
|
const std::string& theToolStep,
|
|
const std::string& theOperation)
|
|
{
|
|
return booleanHistory(readStep(theObjectStep), readStep(theToolStep), theOperation);
|
|
}
|
|
}
|
|
|
|
EMSCRIPTEN_BINDINGS(bitbybit_occt_history)
|
|
{
|
|
emscripten::class_<TopoDS_Shape>("TopoDS_Shape")
|
|
.constructor<>()
|
|
.function("isNull", &TopoDS_Shape::IsNull)
|
|
.function("shapeType", &TopoDS_Shape::ShapeType)
|
|
.function("isSame", &TopoDS_Shape::IsSame);
|
|
emscripten::function("occtVersion", &occtVersion);
|
|
emscripten::function("makeBox", &makeBox);
|
|
emscripten::function("booleanHistory", &booleanHistory);
|
|
emscripten::function("shapeToStep", &writeStep);
|
|
emscripten::function("booleanHistoryFromStep", &booleanHistoryFromStep);
|
|
}
|