[TD]add shape validation tool for debugging

This commit is contained in:
wandererfan
2024-11-21 16:37:00 -05:00
committed by Yorik van Havre
parent b160f1afc0
commit 8f8bdd5c93
6 changed files with 372 additions and 294 deletions

View File

@@ -31,6 +31,7 @@
# include <TopoDS_Iterator.hxx>
# include <TopoDS_Vertex.hxx>
# include <BRepBuilderAPI_Copy.hxx>
#include <BRepCheck_Analyzer.hxx>
#endif
#include <App/Document.h>
@@ -51,7 +52,7 @@
#include "ShapeExtractor.h"
#include "DrawUtil.h"
#include "ShapeUtils.h"
#include "Preferences.h"
using namespace TechDraw;
using DU = DrawUtil;
@@ -62,14 +63,13 @@ using SU = ShapeUtils;
//! Note that point objects will not make it through the hlr/projection process.
std::vector<TopoDS_Shape> ShapeExtractor::getShapes2d(const std::vector<App::DocumentObject*> links)
{
// Base::Console().Message("SE::getShapes2d() - links: %d\n", links.size());
std::vector<TopoDS_Shape> shapes2d;
for (auto& l:links) {
if (is2dObject(l)) {
if (l->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
TopoDS_Shape temp = getLocatedShape(l);
// checkShape on 2d objs?
if (!temp.IsNull()) {
shapes2d.push_back(temp);
}
@@ -83,12 +83,10 @@ std::vector<TopoDS_Shape> ShapeExtractor::getShapes2d(const std::vector<App::Doc
//! fused, include2d should be false as 2d & 3d shapes may not fuse.
TopoDS_Shape ShapeExtractor::getShapes(const std::vector<App::DocumentObject*> links, bool include2d)
{
// Base::Console().Message("SE::getShapes() - links in: %d\n", links.size());
std::vector<TopoDS_Shape> sourceShapes;
for (auto& l:links) {
if (is2dObject(l) && !include2d) {
// Base::Console().Message("SE::getShapes - skipping 2d link: %s\n", l->Label.getValue());
continue;
}
@@ -136,9 +134,11 @@ TopoDS_Shape ShapeExtractor::getShapes(const std::vector<App::DocumentObject*> l
}
else {
auto shape = Part::Feature::getShape(obj);
// if link obj has a shape, we use that shape.
// if source obj has a shape, we use that shape.
if(!SU::isShapeReallyNull(shape) && !isExplodedView) {
sourceShapes.push_back(getLocatedShape(obj));
if (checkShape(obj, shape)) {
sourceShapes.push_back(getLocatedShape(obj));
}
}
else {
std::vector<TopoDS_Shape> shapeList = getShapesFromObject(obj);
@@ -179,17 +179,14 @@ TopoDS_Shape ShapeExtractor::getShapes(const std::vector<App::DocumentObject*> l
}
//it appears that an empty compound is !IsNull(), so we need to check a different way
if (!SU::isShapeReallyNull(comp)) {
// BRepTools::Write(comp, "SEResult.brep"); //debug
return comp;
}
// Base::Console().Error("DEVEL: ShapeExtractor failed to get any shape.\n");
return TopoDS_Shape();
}
std::vector<TopoDS_Shape> ShapeExtractor::getXShapes(const App::Link* xLink)
{
// Base::Console().Message("SE::getXShapes() - %s\n", xLink->getNameInDocument());
std::vector<TopoDS_Shape> xSourceShapes;
if (!xLink) {
return xSourceShapes;
@@ -231,6 +228,9 @@ std::vector<TopoDS_Shape> ShapeExtractor::getXShapes(const App::Link* xLink)
if (ts.isInfinite()) {
shape = stripInfiniteShapes(shape);
}
if (!checkShape(l, shape)) {
continue;
}
// copying the shape prevents "non-orthogonal GTrsf" errors in some versions
// of OCC. Something to do with triangulation of shape??
// it may be that incremental mesh would work here too.
@@ -252,7 +252,8 @@ std::vector<TopoDS_Shape> ShapeExtractor::getXShapes(const App::Link* xLink)
} else {
// link points to a regular object, not another link? no sublinks?
TopoDS_Shape xLinkShape = getShapeFromXLink(xLink);
if (!xLinkShape.IsNull()) {
if (!xLinkShape.IsNull() &&
checkShape(xLink, xLinkShape)) {
// copying the shape prevents "non-orthogonal GTrsf" errors in some versions
// of OCC. Something to do with triangulation of shape??
BRepBuilderAPI_Copy copier(xLinkShape);
@@ -265,7 +266,6 @@ std::vector<TopoDS_Shape> ShapeExtractor::getXShapes(const App::Link* xLink)
// get the located shape for a single childless App::Link
TopoDS_Shape ShapeExtractor::getShapeFromXLink(const App::Link* xLink)
{
// Base::Console().Message("SE::getShapeFromXLink()\n");
Base::Placement xLinkPlacement;
if (xLink->hasPlacement()) {
xLinkPlacement = xLink->getLinkPlacementProperty()->getValue();
@@ -298,21 +298,24 @@ TopoDS_Shape ShapeExtractor::getShapeFromXLink(const App::Link* xLink)
Base::Console().Error("ShapeExtractor failed to retrieve shape from %s\n", xLink->getNameInDocument());
return TopoDS_Shape();
}
return ts.getShape();
if (checkShape(linkedObject, ts.getShape())) {
return ts.getShape();
}
}
return TopoDS_Shape();
}
std::vector<TopoDS_Shape> ShapeExtractor::getShapesFromObject(const App::DocumentObject* docObj)
{
// Base::Console().Message("SE::getShapesFromObject(%s)\n", docObj->getNameInDocument());
std::vector<TopoDS_Shape> result;
const App::GroupExtension* gex = dynamic_cast<const App::GroupExtension*>(docObj);
App::Property* gProp = docObj->getPropertyByName("Group");
App::Property* sProp = docObj->getPropertyByName("Shape");
if (docObj->isDerivedFrom<Part::Feature>()) {
result.push_back(getLocatedShape(docObj));
if (checkShape(docObj, getLocatedShape(docObj))) {
result.push_back(getLocatedShape(docObj));
}
} else if (gex) { //is a group extension
std::vector<App::DocumentObject*> objs = gex->Group.getValues();
std::vector<TopoDS_Shape> shapes;
@@ -326,18 +329,17 @@ std::vector<TopoDS_Shape> ShapeExtractor::getShapesFromObject(const App::Documen
} else if (gProp) { //has a Group property
App::PropertyLinkList* list = dynamic_cast<App::PropertyLinkList*>(gProp);
if (list) {
std::vector<App::DocumentObject*> objs = list->getValues();
std::vector<TopoDS_Shape> shapes;
for (auto& d: objs) {
shapes = getShapesFromObject(d);
if (!shapes.empty()) {
result.insert(result.end(), shapes.begin(), shapes.end());
}
std::vector<App::DocumentObject*> objsAll = list->getValues();
std::vector<TopoDS_Shape> shapesAll;
for (auto& obj : objsAll) {
shapesAll = getShapesFromObject(obj);
result.insert(result.end(), shapesAll.begin(), shapesAll.end());
}
}
} else if (sProp) { //has a Shape property
Part::PropertyPartShape* shape = dynamic_cast<Part::PropertyPartShape*>(sProp);
if (shape) {
Part::PropertyPartShape* shapeProperty = dynamic_cast<Part::PropertyPartShape*>(sProp);
if (shapeProperty &&
checkShape(docObj, getLocatedShape(docObj))) {
result.push_back(getLocatedShape(docObj));
}
}
@@ -346,7 +348,6 @@ std::vector<TopoDS_Shape> ShapeExtractor::getShapesFromObject(const App::Documen
TopoDS_Shape ShapeExtractor::getShapesFused(const std::vector<App::DocumentObject*> links)
{
// Base::Console().Message("SE::getShapesFused()\n");
// get only the 3d shapes and fuse them
TopoDS_Shape baseShape = getShapes(links, false);
if (!baseShape.IsNull()) {
@@ -365,12 +366,10 @@ TopoDS_Shape ShapeExtractor::getShapesFused(const std::vector<App::DocumentObjec
}
baseShape = fusedShape;
}
BRepTools::Write(baseShape, "SEbaseShape.brep");
// if there are 2d shapes in the links they will not fuse with the 3d shapes,
// so instead we return a compound of the fused 3d shapes and the 2d shapes
std::vector<TopoDS_Shape> shapes2d = getShapes2d(links);
BRepTools::Write(DrawUtil::shapeVectorToCompound(shapes2d, false), "SEshapes2d.brep");
if (!shapes2d.empty()) {
shapes2d.push_back(baseShape);
@@ -385,7 +384,6 @@ TopoDS_Shape ShapeExtractor::getShapesFused(const std::vector<App::DocumentObjec
//Infinite shapes can not be projected, so they need to be removed.
TopoDS_Shape ShapeExtractor::stripInfiniteShapes(TopoDS_Shape inShape)
{
// Base::Console().Message("SE::stripInfiniteShapes()\n");
BRep_Builder builder;
TopoDS_Compound comp;
builder.MakeCompound(comp);
@@ -436,7 +434,6 @@ bool ShapeExtractor::isEdgeType(const App::DocumentObject* obj)
bool ShapeExtractor::isPointType(const App::DocumentObject* obj)
{
// Base::Console().Message("SE::isPointType(%s)\n", obj->getNameInDocument());
if (obj) {
Base::Type t = obj->getTypeId();
if (t.isDerivedFrom(Part::Vertex::getClassTypeId())) {
@@ -452,12 +449,10 @@ bool ShapeExtractor::isPointType(const App::DocumentObject* obj)
bool ShapeExtractor::isDraftPoint(const App::DocumentObject* obj)
{
// Base::Console().Message("SE::isDraftPoint()\n");
//if the docObj doesn't have a Proxy property, it definitely isn't a Draft point
App::PropertyPythonObject* proxy = dynamic_cast<App::PropertyPythonObject*>(obj->getPropertyByName("Proxy"));
if (proxy) {
std::string pp = proxy->toString();
// Base::Console().Message("SE::isDraftPoint - pp: %s\n", pp.c_str());
if (pp.find("Point") != std::string::npos) {
return true;
}
@@ -479,7 +474,6 @@ bool ShapeExtractor::isDatumPoint(const App::DocumentObject* obj)
//! get the location of a point object
Base::Vector3d ShapeExtractor::getLocation3dFromFeat(const App::DocumentObject* obj)
{
// Base::Console().Message("SE::getLocation3dFromFeat()\n");
if (!isPointType(obj)) {
return Base::Vector3d(0.0, 0.0, 0.0);
}
@@ -498,8 +492,6 @@ Base::Vector3d ShapeExtractor::getLocation3dFromFeat(const App::DocumentObject*
}
}
// Base::Console().Message("SE::getLocation3dFromFeat - returns: %s\n",
// DrawUtil::formatVector(result).c_str());
return Base::Vector3d(0.0, 0.0, 0.0);
}
@@ -529,3 +521,29 @@ bool ShapeExtractor::isSketchObject(const App::DocumentObject* obj)
return false;
}
//! true if shape fails validity check. A fail here is not a guarantee of later
//! problems, but invalid shapes are known to cause issues with HLR_Algo and boolean ops.
bool ShapeExtractor::checkShape(const App::DocumentObject* shapeObj, TopoDS_Shape shape)
{
if (!Preferences::checkShapesBeforeUse()) {
return true;
}
if (!BRepCheck_Analyzer(shape).IsValid()) {
if (Preferences::debugBadShape()) {
std::stringstream ssFileName;
ssFileName << "BadShape" << shapeObj->Label.getValue() << ".brep";
BRepTools::Write(shape, ssFileName.str().c_str());
}
// this is ok for devs, but there must be a better way to inform the user from somewhere deep in the
// call stack. notification area from App?
Base::Console().Warning(
"ShapeExtractor found a problem shape in %s. Results may be incorrect.\n",
shapeObj->getNameInDocument());
return false;
}
return true;
}