Files
Web_FreeCAD_Bitbybit/native/occt-history/occt_history.cpp

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);
}