Skip to content

Commit

Permalink
Merge pull request #37 from qingfengxia/dev
Browse files Browse the repository at this point in the history
feature #35 dump subshape and error msg for shape failed in Boolean Operation check
  • Loading branch information
qingfengxia authored Jan 20, 2021
2 parents a11c15b + 8a1bbf0 commit 7761cac
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 135 deletions.
153 changes: 109 additions & 44 deletions src/Geom/GeometryShapeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,21 @@ namespace Geom
using namespace PPP;
/// \ingroup Geom
/**
* check error and try to fix it within tolerance, otherwise report error
* FreeCAD Part workbench, ShapeCheck, BOP check
* check shape error and report error
* FreeCAD Part workbench, has a GUI feature for ShapeCheck with tree view
* todo: ShapeHealing toolkit of OCCT has a ShapeAnalysis package
* The base `class Processor` now has report infrastructure, save msg into `myItemReports`
*/
class GeometryShapeChecker : public GeometryProcessor
{
TYPESYSTEM_HEADER();

protected:
/// configurable parameters
bool checkingBooleanOperation = false;
// need a bool parameter to control:
bool suppressBOPCheckFailed = false;

private:
// VectorType<std::shared_ptr<std::string>> myShapeCheckResults;

public:
// using GeometryProcessor::GeometryProcessor;

Expand All @@ -61,29 +60,35 @@ namespace Geom

virtual void processItem(const ItemIndexType i) override final
{
if (itemSuppressed(i))
return;
const TopoDS_Shape& aShape = item(i);
std::string err = checkShape(aShape);
writeItemReport(i, err);
if (err.size() == 0 &&
auto ssp = std::make_shared<std::stringstream>();
if (err.size() > 0)
*ssp << err;

// if basic checkShape has error, then BOPCheck must have fault?
if ( // err.size() == 0 && skip BOP check if preliminary check has failed?
checkingBooleanOperation) // Two common not serious errors are skipped in this BOP check
{
bool hasFault = false;
bool hasBOPFault = false;
try
{
hasFault = BOPSingleCheck(aShape); // will not report error message
hasBOPFault = BOPSingleCheck(i, *ssp); // why most of shape return true even no error msg???
}
catch (const std::exception& e)
{
hasFault = true;
LOG_F(ERROR, "BOP check has exception %s for item %lu ", e.what(), i);
hasBOPFault = true;
LOG_F(ERROR, "BOP check has std::exception %s for item %lu ", e.what(), i);
}
catch (...)
{
hasFault = true;
LOG_F(ERROR, "BOP check has exception for item %lu ", i);
hasBOPFault = true;
LOG_F(ERROR, "BOP check has other exception for item %lu ", i);
}

if (hasFault)
if (hasBOPFault && ssp->str().size() > 0)
{
auto df = generateDumpName("dump_BOPCheckFailed", {i}) + ".brep";
OccUtils::saveShape({item(i)}, df);
Expand All @@ -94,21 +99,27 @@ namespace Geom
}
else
{
LOG_F(ERROR, "BOP check found fault for item %lu, ignore", i);
LOG_F(ERROR, "BOP check found fault for item %lu, ignore this error", i);
}
}
if (ssp->str().size() > 0)
setItemReport(i, ssp);
}
}

/// report, save and display erroneous shape
/// `class Processor::report()`, save and display erroneous shape
/// if this processor 's config has file path in the "report" parameter
virtual void prepareOutput() override final
{
GeometryProcessor::prepareOutput(); // report()
GeometryProcessor::prepareOutput();
}

protected:
/** basic check, geometry reader may has already done this check
* adapted from FreeCAD project: BOPcheck
/** basic check, geometry reader may has already done some of the checks below
* adapted from FreeCAD project:
* https://github.com/FreeCAD/FreeCAD/blob/master/src/Mod/Part/Gui/TaskCheckGeometry.cpp
* passing BRepCheck_Analyzer is a must for boolean operation
* https://dev.opencascade.org/doc/overview/html/specification__boolean_operations.html#specification__boolean_10_1
* */
std::string checkShape(const TopoDS_Shape& _cTopoShape) const
{
Expand All @@ -131,7 +142,7 @@ namespace Geom
switch (val)
{
case BRepCheck_NoError:
// error_msg << ";No error"; // exmty string as a sign of no error
// error_msg << ";No error"; // empty string as a sign of no error
break;
case BRepCheck_InvalidPointOnCurve:
error_msg << ";Invalid point on curve";
Expand Down Expand Up @@ -250,34 +261,60 @@ namespace Geom
return error_msg.str();
}

/// see impl in BOPAlgo_ArgumentAnalyzer.cpp
std::array<const char*, 12> BOPAlgo_StatusNames = {
"CheckUnknown", //
"BadType", // either input shape IsNull()
"SelfIntersect", // self intersection, BOPAlgo_CheckerSI
"TooSmallEdge", // only for BOPAlgo_SECTION
"NonRecoverableFace", // TestRebuildFace()
"IncompatibilityOfVertex", /// TestMergeSubShapes()
"IncompatibilityOfEdge",
"IncompatibilityOfFace",
"OperationAborted", // when error happens in TestSelfInterferences()
"GeomAbs_C0", // continuity
"InvalidCurveOnSurface", //
"NotValid" //
};

const char* getBOPCheckStatusName(const BOPAlgo_CheckStatus& status)
{
size_t index = static_cast<size_t>(status);
assert(index >= 0 || index < BOPAlgo_StatusNames.size());
return BOPAlgo_StatusNames[index];
}

/// check single TopoDS_Shape, adapted from FreeCAD and some other online forum
// two common, notorious errors should be turn off curveOnSurfaceMode, continuityMode
// didn't use BRepAlgoAPI_Check because it calls BRepCheck_Analyzer itself and
// it doesn't give us access to it. so I didn't want to run BRepCheck_Analyzer twice to get invalid results.
// BOPAlgo_ArgumentAnalyzer can check 2 objects with respect to a boolean op.
// this is left for another time.
bool BOPSingleCheck(const TopoDS_Shape& shapeIn)
/** use `BOPAlgo_ArgumentAnalyzer` to check 2 shapes with respect to a boolean operation.
two common errors are `curveOnSurfaceMode`, `continuityMode`
it is not clear which one cause BoP failure: `selfInterference`, `curveOnSurfaceMode`
Note: do NOT use BRepAlgoAPI_Check ( BRepCheck_Analyzer )
*/
bool BOPSingleCheck(const ItemIndexType i, std::stringstream& ss)
{
bool runSingleThreaded = true; //
// bool logErrors = true;
const TopoDS_Shape& aShape = item(i);
bool runSingleThreaded = true; // parallel is done on solid shape level for PPP, not on subshape level

bool argumentTypeMode = true;
bool selfInterMode = true; // self interference, for single solid should be no such error
bool smallEdgeMode = true;
bool smallEdgeMode = true; // only needed for de-feature?

bool continuityMode = false; // BOPAlgo_GeomAbs_C0, it should not cause BOP failure?
bool tangentMode = false; // not implemented in OpenCASCADE

bool rebuildFaceMode = true;
bool continuityMode = false; // BOPAlgo_GeomAbs_C0
bool tangentMode = true; // not implemented in OpenCASCADE
bool mergeVertexMode = true;
bool mergeVertexMode = true; // leader to `BOPAlgo_IncompatibilityOfEdge`
bool mergeEdgeMode = true;
bool curveOnSurfaceMode = false; // BOPAlgo_InvalidCurveOnSurface: tolerance compatability check

// I don't why we need to make a copy, but it doesn't work without it.
// BRepAlgoAPI_Check also makes a copy of the shape.
TopoDS_Shape BOPCopy = BRepBuilderAPI_Copy(shapeIn).Shape();
// FreeCAD develper's note: I don't why we need to make a copy, but it doesn't work without it.
/// maybe, mergeVertexMode() will modify the shape to check
TopoDS_Shape BOPCopy = BRepBuilderAPI_Copy(aShape).Shape();
BOPAlgo_ArgumentAnalyzer BOPCheck;

// BOPCheck.StopOnFirstFaulty() = true; //this doesn't run any faster but gives us less results.
// BOPCheck.StopOnFirstFaulty() = true; //this doesn't run any faster but gives us less results.
// BOPCheck.SetFuzzyValue();
BOPCheck.SetShape1(BOPCopy);
// BOPCheck.SetOperation(); // by default, set to BOPAlgo_UNKNOWN
// all settings are false by default. so only turn on what we want.
BOPCheck.ArgumentTypeMode() = argumentTypeMode;
BOPCheck.SelfInterMode() = selfInterMode;
Expand All @@ -287,19 +324,47 @@ namespace Geom
BOPCheck.ContinuityMode() = continuityMode;
#endif
#if OCC_VERSION_HEX >= 0x060900
BOPCheck.SetParallelMode(!runSingleThreaded); // this doesn't help for speed right now(occt 6.9.1).
BOPCheck.SetRunParallel(!runSingleThreaded); // performance boost, use all available cores
BOPCheck.SetParallelMode(!runSingleThreaded);
BOPCheck.SetRunParallel(!runSingleThreaded);

BOPCheck.TangentMode() = tangentMode; // these 4 new tests add about 5% processing time.
BOPCheck.MergeVertexMode() = mergeVertexMode;
BOPCheck.MergeVertexMode() = mergeVertexMode; // will it modify the shape to check?
BOPCheck.MergeEdgeMode() = mergeEdgeMode;
BOPCheck.CurveOnSurfaceMode() = curveOnSurfaceMode;
#endif

BOPCheck.Perform();
if (!BOPCheck.HasFaulty())
return false;
return true;
BOPCheck.Perform(); // this perform() has internal try-catch block
if (BOPCheck.HasFaulty())
{
const BOPAlgo_ListOfCheckResult& BOPResults = BOPCheck.GetCheckResult();
BOPAlgo_ListIteratorOfListOfCheckResult BOPResultsIt(BOPResults);
for (size_t j = 0; BOPResultsIt.More(); BOPResultsIt.Next(), j++)
{
const BOPAlgo_CheckResult& current = BOPResultsIt.Value();
if (current.GetCheckStatus() != BOPAlgo_CheckUnknown)
{
ss << ";" << getBOPCheckStatusName(current.GetCheckStatus());
dumpSubshape(i, j, current);
}
}
return true; // has failure
}
return false; // no fault
}

void dumpSubshape(const ItemIndexType i, const ItemIndexType subId, const BOPAlgo_CheckResult& result) const
{
const auto& faultyShapes1 = result.GetFaultyShapes1();
TopTools_ListIteratorOfListOfShape faultyShapes1It(faultyShapes1);

for (size_t k = 0; faultyShapes1It.More(); faultyShapes1It.Next(), k++)
{
auto df = generateDumpName("dump_subshape_BOPCheckFailed", {i, subId, k}) + ".brep";
const auto& subshape = faultyShapes1It.Value();
OccUtils::saveShape(subshape, df);
}
}

}; // end of class

} // namespace Geom
Expand Down
74 changes: 73 additions & 1 deletion src/Geom/Readme.md
Original file line number Diff line number Diff line change
@@ -1 +1,73 @@
## Design of Geom module
# Design of Geom module

Feature overview is located in wiki folder.

## CLI design

subcommand style CLI will be enforced in the future.

https://dzone.com/articles/multi-level-argparse-python
https://planetask.medium.com/command-line-subcommands-with-pythons-argparse-4dbac80f7110

FIXED:
`working-dir` cause error, because input file has not been translated into abspath, then change cwd to `working-dir`, so input file can not been found. it has been fixed.

TODO:
`dataStorage` in config.json, `"workingDir": args.workingDir,`
a better name `data-dir` should be used instead of `working-dir`

## Imprint

currently merge is done by a single thread

### merge is delayed until writing result file

https://github.com/ukaea/parallel-preprocessor/issues/23

merge will invalidate `Item()`

## Scaling

### CAD length units
In CAD world, the length unit by default im "MM", while in CAE world, ISO meter is the default unit.

Input geometry unit, should be saved in GeometryData class by GeometryReader, and check during write output.

1. OpenCASCADE brep file has no meta data for unit

2. unit or scale for SAT file format

> open your sat file with a text editor, change the first parameter of line 3 to "1" from "1000". This change will change the file unit from Meter to Millimeter.
3. Parasolid-XT Meters and radians by convention
https://docs.cadexchanger.com/sdk/sdk_datamodel_units_page.html

### change output unit will cause scaling
add a new arg "--output-unit M" in python CLI interface

### delayed scaling until writing
calc scale ratio according to inputUnit and outputUnit

Transform will invalidate mesh triangulation attached to shape !!!

Transform maybe done in parallel, before merge, not sure it is worth of such operation?


1. OpenCASCADE STEP writer
`Interface_Static_SetCVal("Interface_Static_SetCVal("write.step.unit","MM")`

`outputUnit` in GeometryWriter config can scale, based on the `inputUnit`


2. OpenCASCADE brep write

`TopoDS_Shape` this is unitless data structure, to scale, using `gp_Trsf`

```c++
gp_Trsf ts;
ts.SetScale(origin, scale);
return BRepBuilderAPI_Transform(from, ts, true);
```

## To switch between CAD kernel
make new GeometryData derived class, put all OccUtils function as class virtual member functions, but there is performance penalty (virtual functions)
Loading

0 comments on commit 7761cac

Please sign in to comment.