[TD]add shape validation tool for debugging
This commit is contained in:
committed by
Yorik van Havre
parent
b160f1afc0
commit
8f8bdd5c93
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user