From 2326d52a781d3af8ab0258af913a293ee0917419 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 20 Feb 2024 14:43:02 +0100 Subject: [PATCH] [TD]Refactor LTNP correction code for Dimensions - additional geometry types - add method to handle line like bsplines - handle deleted ref body feature - add test for empty savedGeometry - add switch for matcher/LTNP on/off --- src/Mod/TechDraw/App/CMakeLists.txt | 2 + src/Mod/TechDraw/App/DimensionAutoCorrect.cpp | 596 ++++++++++++ src/Mod/TechDraw/App/DimensionAutoCorrect.h | 101 ++ src/Mod/TechDraw/App/DimensionReferences.cpp | 135 ++- src/Mod/TechDraw/App/DimensionReferences.h | 32 +- src/Mod/TechDraw/App/DrawViewDimension.cpp | 918 +++++++++--------- src/Mod/TechDraw/App/DrawViewDimension.h | 209 ++-- src/Mod/TechDraw/App/DrawViewPart.cpp | 2 +- src/Mod/TechDraw/App/Geometry.cpp | 18 + src/Mod/TechDraw/App/Geometry.h | 2 + src/Mod/TechDraw/App/GeometryMatcher.cpp | 111 ++- src/Mod/TechDraw/App/GeometryMatcher.h | 33 +- src/Mod/TechDraw/App/Preferences.cpp | 10 + src/Mod/TechDraw/App/Preferences.h | 2 + src/Mod/TechDraw/Gui/Command.cpp | 3 +- src/Mod/TechDraw/Gui/QGIViewDimension.cpp | 3 +- 16 files changed, 1505 insertions(+), 672 deletions(-) create mode 100644 src/Mod/TechDraw/App/DimensionAutoCorrect.cpp create mode 100644 src/Mod/TechDraw/App/DimensionAutoCorrect.h diff --git a/src/Mod/TechDraw/App/CMakeLists.txt b/src/Mod/TechDraw/App/CMakeLists.txt index 994a0770173e..7f2736df879f 100644 --- a/src/Mod/TechDraw/App/CMakeLists.txt +++ b/src/Mod/TechDraw/App/CMakeLists.txt @@ -106,6 +106,8 @@ SET(Draw_SRCS DimensionFormatter.h GeometryMatcher.cpp GeometryMatcher.h + DimensionAutoCorrect.cpp + DimensionAutoCorrect.h DrawViewBalloon.cpp DrawViewBalloon.h DrawViewSection.cpp diff --git a/src/Mod/TechDraw/App/DimensionAutoCorrect.cpp b/src/Mod/TechDraw/App/DimensionAutoCorrect.cpp new file mode 100644 index 000000000000..98d74fa48f15 --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionAutoCorrect.cpp @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/*************************************************************************** + * Copyright (c) 2023 WandererFan * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ +// a class to validate and correct dimension references + +// the dimension reference auto correct algo: +// +// when a dimension is created, the shape of each reference is saved, so in addition +// to the dimensionedObject + subElement reference, we also keep a Part::TopoShape copy +// of the reference. +// +// when we later use that dimension, we check its references as follows: +// for each reference: +// // auto correct phase 1 +// if ref.currentGeometry == ref.savedGeometry: +// // the reference points to the same geometry as before, so we +// // so we consider this to be correct. same geometry, same index case. +// continue +// else: +// // search all the source shapes for a subelement with the exact same +// // geometry. same geometry, different index case. +// newRef = searchForExactSameGeometry(ref) // geometry matcher +// if newRef: +// // substitute the reference we just found in place of the old +// // reference +// replace(ref, newRef) +// else: +// // auto correct phase 2 +// // we don't have any geometry that is identical to our saved geometry. +// // finding a match now becomes guess work. we have to find the most +// // similar geometry (with at least some level of same-ness) and use +// // that to rebuild our reference. +// // we do not have a good algo for searchForMostSimilarGeometry() yet. +// newRef = searchForMostSimilarGeometry(ref) // geometry guesser +// if newRef: +// replace(ref, newRef) +// else: +// //we can't fix this + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#endif + +#include + +#include +#include +#include + +#include + +#include "GeometryMatcher.h" +#include "DimensionReferences.h" +#include "DimensionGeometry.h" +#include "DimensionAutoCorrect.h" +#include "DrawUtil.h" +#include "Preferences.h" + +using namespace TechDraw; +using DU = DrawUtil; + +//! true if references point to valid geometry and the valid geometry matches the +//! corresponding saved geometry. this method does not correct anything, it just +//! verifies if the references point to the same geometry as when the reference +//! was created. +bool DimensionAutoCorrect::referencesHaveValidGeometry(std::vector& referenceState) const +{ + // Base::Console().Message("DAC::referencesHaveValidGeometry()\n"); + + ReferenceVector refsAll = getDimension()->getEffectiveReferences(); + const std::vector savedGeometry = getDimension()->SavedGeometry.getValues(); + + if (savedGeometry.empty() || savedGeometry.size() != refsAll.size()) { + // this must be an old document without savedGeometry property. We can not + // validate the references in this case so we hope they are valid. + referenceState = std::vector(refsAll.size(), true); + return true; + } + + bool result {true}; + size_t iRef {0}; + for (auto& entry : refsAll) { + if (entry.hasGeometry()) { + // entry points to something, is it the correct geom? + if (isMatchingGeometry(entry, savedGeometry.at(iRef))) { + referenceState.emplace_back(true); + } + else { + result = false; + referenceState.emplace_back(false); + } + } + else { + result = false; + referenceState.emplace_back(false); + } + } + return result; +} + +//! try to correct references that point to non-existent geometry or when the saved +//! geometry does not match the current geometry the reference points to. +//! referenceState is the output of a previous use of referencesHaveValidGeometry. +bool DimensionAutoCorrect::autocorrectReferences(std::vector& referenceState, + ReferenceVector& repairedRefs) const +{ + // Base::Console().Message("DAC::autocorrectReferences()\n"); + if (!Preferences::autoCorrectDimRefs()) { + return false; + } + + bool result {true}; + ReferenceVector refsAll = getDimension()->getEffectiveReferences(); + const std::vector savedGeometry = getDimension()->SavedGeometry.getValues(); + if (savedGeometry.empty() || savedGeometry.size() != refsAll.size()) { + // this must be an old document without savedGeometry property. We can not + // validate the references in this case. + return false; + } + + std::vector referenceGeometry; + for (auto& entry : refsAll) { + if (entry.hasGeometry()) { + referenceGeometry.push_back(entry.asTopoShape()); + } + else { + referenceGeometry.push_back(Part::TopoShape()); + } + } + + size_t iRef {0}; + for (const auto& state : referenceState) { + if (state) { + // ref points to valid geometry that matches saved geometry + referenceState.at(iRef) = true; + repairedRefs.push_back(refsAll.at(iRef)); + iRef++; + continue; + } + + Part::TopoShape temp = savedGeometry.at(iRef); + if (temp.isNull()) { + result = false; + referenceState.at(iRef) = false; + repairedRefs.push_back(refsAll.at(iRef)); + iRef++; + // we could exit here instead of checking all the refs? + continue; + } + + // this ref does not point to valid geometry or + // the geometry it points to does not match the saved geometry + + ReferenceEntry fixedRef = refsAll.at(iRef); + + // first, look for an exact match to the saved geometry + bool success = fix1GeomExact(fixedRef, savedGeometry.at(iRef).getShape()); + if (success) { + // we did find a match + referenceState.at(iRef) = true; + repairedRefs.push_back(fixedRef); + iRef++; + continue; + } + + // we did not find an exact match, so check for an Similar match + success = fix1GeomSimilar(fixedRef, savedGeometry.at(iRef).getShape()); + if (success) { + // we did find an Similar match + referenceState.at(iRef) = true; + repairedRefs.push_back(fixedRef); + iRef++; + continue; + } + + // we did not find an Similar match the geometry + result = false; + referenceState.at(iRef) = false; + repairedRefs.push_back(fixedRef); + iRef++; + // we could exit here + } + + return result; +} + +//! fix a single reference with an exact match to geomToFix +bool DimensionAutoCorrect::fix1GeomExact(ReferenceEntry& refToFix, TopoDS_Shape geomToFix) const +{ + // Base::Console().Message("DAC::fix1GeomExact()\n"); + ReferenceEntry fixedRef = refToFix; + Part::TopoShape topoShapeToFix(geomToFix); + bool success {false}; + if (refToFix.is3d()) { + if (!refToFix.getObject() && m_3dObjectCache.empty()) { + return false; + } + if (geomToFix.ShapeType() == TopAbs_VERTEX) { + success = findExactVertex3d(refToFix, topoShapeToFix); + } + else { + success = findExactEdge3d(refToFix, topoShapeToFix); + } + } + else { + if (geomToFix.ShapeType() == TopAbs_VERTEX) { + success = findExactVertex2d(refToFix, topoShapeToFix); + } + else { + success = findExactEdge2d(refToFix, topoShapeToFix); + } + } + return success; +} + + +//! fix a single reference with an Similar match to geomToFix +bool DimensionAutoCorrect::fix1GeomSimilar(ReferenceEntry& refToFix, TopoDS_Shape geomToFix) const +{ + // Base::Console().Message("DAC::fix1GeomSimilar()\n"); + Part::TopoShape topoShapeToFix(geomToFix); + bool success {false}; + if (refToFix.is3d()) { + if (!refToFix.getObject() && m_3dObjectCache.empty()) { + // can't fix this. nothing to compare. + return false; + } + if (geomToFix.ShapeType() == TopAbs_VERTEX) { + success = findSimilarVertex3d(refToFix, topoShapeToFix); + } + else { + success = findSimilarEdge3d(refToFix, topoShapeToFix); + } + } + else { + if (geomToFix.ShapeType() == TopAbs_VERTEX) { + success = findSimilarVertex2d(refToFix, topoShapeToFix); + } + else { + success = findSimilarEdge2d(refToFix, topoShapeToFix); + } + } + return success; +} + + +//! search the view for a 2d vertex that is the same as the saved reference geometry +//! and return a reference pointing to the matching vertex +bool DimensionAutoCorrect::findExactVertex2d(ReferenceEntry& refToFix, + Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findExactVertex2d()\n"); + getMatcher()->setPointTolerance(EWTOLERANCE); + auto refObj = refToFix.getObject(); + auto refDvp = dynamic_cast(refObj); + if (refDvp) { + ReferenceEntry fixedRef = searchViewForVert(refDvp, refGeom); + if (fixedRef.getObject()) { + refToFix = fixedRef; + return true; + } + } + // no match + return false; +} + +//! search the view for a 2d edge that is the same as the saved reference geometry +//! and return a reference pointing to the matching edge. +bool DimensionAutoCorrect::findExactEdge2d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findExactEdge2d()\n"); + double scale = getDimension()->getViewPart()->getScale(); + BaseGeomPtrVector gEdgeAll = getDimension()->getViewPart()->getEdgeGeometry(); + int iEdge {0}; + for (auto& edge : gEdgeAll) { + Part::TopoShape temp = edge->asTopoShape(scale); + bool isSame = getMatcher()->compareGeometry(refGeom, temp); + if (isSame) { + refToFix.setSubName(std::string("Edge") + std::to_string(iEdge)); + return true; + } + iEdge++; + } + + // no match, return the input reference + return false; +} + +//! search the model for a 3d vertex that is the same as the saved reference geometry +//! and return a reference pointing to the matching vertex +bool DimensionAutoCorrect::findExactVertex3d(ReferenceEntry& refToFix, + Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findExactVertex3d()\n"); + getMatcher()->setPointTolerance(EWTOLERANCE); + + // try the referenced object + auto refObj = refToFix.getObject(); + if (refObj) { + ReferenceEntry fixedRef = searchObjForVert(refObj, refGeom); + if (fixedRef.getObject()) { + refToFix = fixedRef; + return true; + } + } + + // not match in refObj (or no refObj!) + for (auto& objectName : m_3dObjectCache) { + auto object3d = getDimension()->getDocument()->getObject(objectName.c_str()); + ReferenceEntry fixedRef = searchObjForVert(object3d, refGeom); + if (fixedRef.getObject()) { + refToFix = fixedRef; + return true; + } + } + + return false; +} + +//! search the model for a 3d edge that is the same as the saved reference geometry +//! and return a reference pointing to the matching edge. +bool DimensionAutoCorrect::findExactEdge3d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findExactEdge3d() - cache: %d\n", m_3dObjectCache.size()); + // first, try to find a match in the referenced object + auto refObj = refToFix.getObject(); + if (refObj) { + ReferenceEntry fixedRef = searchObjForEdge(refObj, refGeom); + if (fixedRef.getObject()) { + refToFix = fixedRef; + return true; + } + } + + // no match in refObj, so now search the cached objects + for (auto& objectName : m_3dObjectCache) { + auto object3d = getDimension()->getDocument()->getObject(objectName.c_str()); + auto shape3d = Part::Feature::getShape(object3d); + auto edgesAll = getDimension()->getEdges(shape3d); + size_t iEdge {1}; + for (auto& edge : edgesAll) { + if (getMatcher()->compareGeometry(edge, refGeom)) { + // found a match! + refToFix.setObjectName(objectName); + refToFix.setObject(object3d); + refToFix.setSubName(std::string("Edge") + std::to_string(iEdge)); + return true; + } + iEdge++; + } + } + return false; +} + +//! search the view for a vertex that is within a tolerance of the saved reference geometry +//! and return a reference pointing to the matching vertex +bool DimensionAutoCorrect::findSimilarVertex2d(ReferenceEntry& refToFix, + Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findSimilarVertex2d()\n"); + (void)refToFix; + (void)refGeom; + Base::Console().Message("DAC::findSimilarVertex2d is not implemented yet\n"); + return false; +} + +//! search the view for a 2d edge that is similar to the saved reference geometry +//! and return a reference pointing to the similar edge. +bool DimensionAutoCorrect::findSimilarEdge2d(ReferenceEntry& refToFix, + Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findSimilarEdge2d()\n"); + (void)refToFix; + (void)refGeom; + Base::Console().Message("DAC::findSimilarEdge2d is not implemented yet\n"); + return false; +} + +//! search the referenced 3d object and the object cache for a vertex that is within +//! a tolerance of the saved reference geometry and return a reference pointing +//! to the matching vertex +bool DimensionAutoCorrect::findSimilarVertex3d(ReferenceEntry& refToFix, + Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findSimilarVertex3d()\n"); + (void)refToFix; + (void)refGeom; + Base::Console().Message("DAC::findSimilarVertex3d is not implemented yet\n"); + return false; +} + +//! search the referenced 3d object and the object cache for an edge that is +//! similar to the saved reference geometry and return a reference pointing +//! to the similar edge +bool DimensionAutoCorrect::findSimilarEdge3d(ReferenceEntry& refToFix, + Part::TopoShape refGeom) const +{ + // Base::Console().Message("DAC::findSimilarEdge3d(%s)\n", refToFix.getObjectName().c_str()); + (void)refToFix; + (void)refGeom; + Base::Console().Message("DAC::findSimilarEdge3d is not implemented yet\n"); + return false; +} + +//! compare the geometry pointed to by a reference to the corresponding saved geometry +bool DimensionAutoCorrect::isMatchingGeometry(ReferenceEntry ref, + Part::TopoShape savedGeometry) const +{ + Part::TopoShape temp = ref.asTopoShape(); + if (temp.isNull()) { + // this shouldn't happen as we already know that this ref points to valid geometry + return false; + } + if (getMatcher()->compareGeometry(temp, savedGeometry)) { + // reference still points to the same geometry + return true; + } + + return false; +} + +//! search obj (3d object with a shape) for a match to refVertex. This is always +//! an exact match for phase 1 (GeometryMatcher), but in phase 2 (GeometryGuesser) +//! a similar match will be allowed. +ReferenceEntry DimensionAutoCorrect::searchObjForVert(App::DocumentObject* obj, + Part::TopoShape refVertex, + bool exact) const +{ + (void)exact; + auto shape3d = Part::Feature::getShape(obj); + if (shape3d.IsNull()) { + // how to handle this? + return {}; + } + auto vertsAll = getDimension()->getVertexes(shape3d); + size_t iVert {1}; + for (auto& vert : vertsAll) { + bool isSame = getMatcher()->compareGeometry(refVertex, vert); + if (isSame) { + auto newSubname = std::string("Vertex") + std::to_string(iVert); + return {obj, newSubname, getDimension()->getDocument()}; + } + iVert++; + } + return {}; +} + + +//! search View (2d part display) for a match to refVertex. This can be an +//! exact or Similar match depending on the setting of exact. +ReferenceEntry DimensionAutoCorrect::searchViewForVert(DrawViewPart* obj, + Part::TopoShape refVertex, + bool exact) const +{ + (void)exact; + double scale = getDimension()->getViewPart()->getScale(); + std::vector gVertexAll = + getDimension()->getViewPart()->getVertexGeometry(); + getMatcher()->setPointTolerance(EWTOLERANCE); + int iVertex = 0; + for (auto& vert : gVertexAll) { + Part::TopoShape temp = vert->asTopoShape(scale); + bool isSame = getMatcher()->compareGeometry(refVertex, temp); + if (isSame) { + auto newSubname = std::string("Vertex") + std::to_string(iVertex); + return {obj, newSubname, getDimension()->getDocument()}; + } + iVertex++; + } + return {}; +} + +//! search View (2d part display) for an exact match to refEdge. +ReferenceEntry DimensionAutoCorrect::searchViewForExactEdge(DrawViewPart* obj, + Part::TopoShape refEdge) const +{ + // Base::Console().Message("DAC::searchViewForExactEdge()\n"); + double scale = getDimension()->getViewPart()->getScale(); + auto gEdgeAll = getDimension()->getViewPart()->getEdgeGeometry(); + int iEdge {0}; + for (auto& edge : gEdgeAll) { + Part::TopoShape temp = edge->asTopoShape(scale); + bool isSame = getMatcher()->compareGeometry(refEdge, temp); + if (isSame) { + auto newSubname = std::string("Edge") + std::to_string(iEdge); + return {obj, newSubname, getDimension()->getDocument()}; + } + iEdge++; + } + return {}; +} + + +//! search View (2d part display) for an edge that is similar to refEdge +ReferenceEntry DimensionAutoCorrect::searchViewForSimilarEdge(DrawViewPart* obj, + Part::TopoShape refEdge) const +{ + // Base::Console().Message("DAC::searchViewForSimilarEdge()\n"); + (void)obj; + (void)refEdge; + Base::Console().Message("DAC::searchViewForSimilarEdge is not implemented yet\n"); + return {}; +} + +//! search model for for a 3d edge that is a match to refEdge +//! note that only the exact match is implemented in phase 1 +ReferenceEntry DimensionAutoCorrect::searchObjForEdge(App::DocumentObject* obj, + Part::TopoShape refEdge, + bool exact) const +{ + // Base::Console().Message("DAC::searchObjForEdge(%s)\n", obj->Label.getValue()); + (void)exact; + auto shape3d = Part::Feature::getShape(obj); + if (shape3d.IsNull()) { + // how to handle this? + // Base::Console().Message("DAC::searchObjForEdge - object shape is null\n"); + return {}; + } + auto edgesAll = getDimension()->getEdges(shape3d); + size_t iEdge {1}; + for (auto& edge : edgesAll) { + bool isSame = getMatcher()->compareGeometry(refEdge, edge); + if (isSame) { + auto newSubname = std::string("Edge") + std::to_string(iEdge); + return {obj, newSubname, getDimension()->getDocument()}; + } + iEdge++; + } + return {}; +} + +//! rebuild 3d references from saved geometry. returns true is all references +//! have been repaired +bool DimensionAutoCorrect::fixBrokenReferences(ReferenceVector& fixedReferences) const +{ + // Base::Console().Message("DAC::fixBrokenReferences()\n"); + bool success {true}; + const std::vector savedGeometry = getDimension()->SavedGeometry.getValues(); + int iGeom {0}; + for (auto& geom : savedGeometry) { + if (fixedReferences.at(iGeom).hasGeometry()) { + iGeom++; + continue; + } + // + TopoDS_Shape geomShape = geom.getShape(); + for (auto& objectName : m_3dObjectCache) { + auto object3d = getDimension()->getDocument()->getObject(objectName.c_str()); + if (!object3d) { + // cached object has been deleted + continue; + } + // TODO: do we need to check for Similar matches here too? + ReferenceEntry newRef; + if (geomShape.ShapeType() == TopAbs_VERTEX) { + newRef = searchObjForVert(object3d, geomShape); + fixedReferences.at(iGeom) = newRef; + } + else { + newRef = searchObjForEdge(object3d, geomShape); + fixedReferences.at(iGeom) = newRef; + } + fixedReferences.at(iGeom) = newRef; + if (!newRef.getObject()) { + success = false; + } + } + } + return success; +} + + +GeometryMatcher* DimensionAutoCorrect::getMatcher() const +{ + return getDimension()->getMatcher(); +} diff --git a/src/Mod/TechDraw/App/DimensionAutoCorrect.h b/src/Mod/TechDraw/App/DimensionAutoCorrect.h new file mode 100644 index 000000000000..a3e154d49034 --- /dev/null +++ b/src/Mod/TechDraw/App/DimensionAutoCorrect.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/*************************************************************************** + * Copyright (c) 2023 WandererFan * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ +// a class to validate and correct dimension references + +#ifndef DIMAUTOCORRECT_H +#define DIMAUTOCORRECT_H + +#include + +#include + +namespace Part +{ +class TopoShape; +} + +namespace TechDraw +{ +class GeometryMatcher; + + +class TechDrawExport DimensionAutoCorrect +{ +public: + DimensionAutoCorrect() + {} + explicit DimensionAutoCorrect(DrawViewDimension* dim) + { + m_dimension = dim; + } + ~DimensionAutoCorrect() = default; + + bool referencesHaveValidGeometry(std::vector& referenceState) const; + bool autocorrectReferences(std::vector& referenceState, + ReferenceVector& repairedRefs) const; + + void set3dObjectCache(std::set cache) + { + m_3dObjectCache = cache; + } + bool fixBrokenReferences(ReferenceVector& fixedReferences) const; + +private: + bool fix1GeomExact(ReferenceEntry& refToFix, TopoDS_Shape geomToFix) const; + bool fix1GeomSimilar(ReferenceEntry& refToFix, TopoDS_Shape geomToFix) const; + + bool findExactVertex2d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + bool findExactEdge2d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + bool findExactVertex3d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + bool findExactEdge3d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + + bool findSimilarVertex2d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + bool findSimilarEdge2d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + bool findSimilarVertex3d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + bool findSimilarEdge3d(ReferenceEntry& refToFix, Part::TopoShape refGeom) const; + + ReferenceEntry + searchObjForVert(App::DocumentObject* obj, Part::TopoShape refVertex, bool exact = true) const; + ReferenceEntry + searchViewForVert(DrawViewPart* obj, Part::TopoShape refVertex, bool exact = true) const; + ReferenceEntry + searchObjForEdge(App::DocumentObject* obj, Part::TopoShape refEdge, bool exact = true) const; + + ReferenceEntry searchViewForExactEdge(DrawViewPart* obj, Part::TopoShape refEdge) const; + ReferenceEntry searchViewForSimilarEdge(DrawViewPart* obj, Part::TopoShape refEdge) const; + + + bool isMatchingGeometry(ReferenceEntry ref, Part::TopoShape savedGeometry) const; + + DrawViewDimension* getDimension() const + { + return m_dimension; + } + GeometryMatcher* getMatcher() const; + + DrawViewDimension* m_dimension; + std::set m_3dObjectCache; +}; + +} // end namespace TechDraw +#endif diff --git a/src/Mod/TechDraw/App/DimensionReferences.cpp b/src/Mod/TechDraw/App/DimensionReferences.cpp index 6ff8cb462eba..9f82c903be7a 100644 --- a/src/Mod/TechDraw/App/DimensionReferences.cpp +++ b/src/Mod/TechDraw/App/DimensionReferences.cpp @@ -31,6 +31,8 @@ #include #include +#include +#include #include #include #include @@ -44,16 +46,65 @@ using namespace TechDraw; using DU = DrawUtil; + +ReferenceEntry::ReferenceEntry( App::DocumentObject* docObject, std::string subName, App::Document* document) +{ + setObject(docObject); + setSubName(subName); + setDocument(document); + if (docObject) { + setObjectName(docObject->getNameInDocument()); + if (document == nullptr) { + setDocument(docObject->getDocument()); + } + } +} + +ReferenceEntry::ReferenceEntry(const ReferenceEntry& other) +{ + setObject(other.getObject()); + setSubName(other.getSubName()); + setObjectName(other.getObjectName()); + setDocument(other.getDocument()); +} + + + +ReferenceEntry& ReferenceEntry::operator=(const ReferenceEntry& otherRef) +{ + setObject(otherRef.getObject()); + setSubName(otherRef.getSubName()); + setObjectName(otherRef.getObjectName()); + setDocument(otherRef.getDocument()); + return *this; +} + + TopoDS_Shape ReferenceEntry::getGeometry() const { -// Base::Console().Message("RE::getGeometry() - obj: %s sub: %s\n", -// getObject()->getNameInDocument(), getSubName()); + // Base::Console().Message("RE::getGeometry() - objectName: %s sub: **%s**\n", + // getObjectName(), getSubName()); + // first, make sure the object has not been deleted! + App::DocumentObject* obj = getDocument()->getObject(getObjectName().c_str()); + if (!obj) { + Base::Console().Message("RE::getGeometry - %s no longer exists!\n", getObjectName().c_str()); + return {}; + } + + if (getSubName().empty()) { + Base::Console().Message("RE::getGeometry - Reference has no subelement!\n"); + return {}; + } + if ( getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) ) { + // Base::Console().Message("RE::getGeometry - getting 2d geometry\n"); std::string gType; try { auto dvp = static_cast(getObject()); gType = geomType(); if (gType == "Vertex") { + // getVertex throws on not found, but we want to return null + // shape auto vgeom = dvp->getVertex(getSubName()); return vgeom->getOCCVertex(); } @@ -67,11 +118,12 @@ TopoDS_Shape ReferenceEntry::getGeometry() const } } catch (...) { -// Base::Console().Message("RE::getGeometry - no shape for dimension reference - gType: %s\n", gType.c_str()); + Base::Console().Message("RE::getGeometry - no shape for dimension 2d reference - gType: **%s**\n", gType.c_str()); return {}; } } + // Base::Console().Message("RE::getGeometry - getting 2d geometry\n"); Part::TopoShape shape = Part::Feature::getTopoShape(getObject()); auto geoFeat = dynamic_cast(getObject()); if (geoFeat) { @@ -100,13 +152,15 @@ std::string ReferenceEntry::getSubName(bool longForm) const App::DocumentObject* ReferenceEntry::getObject() const { - // For PartDesign objects, when the reference is created from a selection, - // the SelectionObject is a Feature within the Body. - PartDesign::Body* pdBody = PartDesign::Body::findBodyOf(m_object); - if (pdBody && pdBody->Tip.getValue()) { - return pdBody->Tip.getValue(); + if (!getDocument()) { + return nullptr; } - return m_object; + App::DocumentObject* obj = getDocument()->getObject(getObjectName().c_str()); + if (!obj) { + return nullptr; + } + + return obj; } Part::TopoShape ReferenceEntry::asTopoShape() const @@ -114,7 +168,9 @@ Part::TopoShape ReferenceEntry::asTopoShape() const // Base::Console().Message("RE::asTopoShape()\n"); TopoDS_Shape geom = getGeometry(); if (geom.IsNull()) { - throw Base::RuntimeError("Dimension Reference has null geometry"); + // throw Base::RuntimeError("Dimension Reference has null geometry"); + Base::Console().Message("RE::asTopoShape - reference geometry is null\n"); + return {}; } if (geom.ShapeType() == TopAbs_VERTEX) { TopoDS_Vertex vert = TopoDS::Vertex(geom); @@ -153,6 +209,7 @@ Part::TopoShape ReferenceEntry::asTopoShapeEdge(TopoDS_Edge &edge) const std::string ReferenceEntry::geomType() const { + // Base::Console().Message("RE::geomType() - subName: **%s**\n", getSubName().c_str()); return DrawUtil::getGeomTypeFromName(getSubName()); } @@ -163,15 +220,61 @@ bool ReferenceEntry::isWholeObject() const bool ReferenceEntry::is3d() const { - return !getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()); + if (getObject() && + getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + !getSubName().empty()) { + // this is a well formed 2d reference + return false; + } + + if (getObject() && + getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && + getSubName().empty()) { + // this is a broken 3d reference, so it should be treated as 3d + return true; + } + + // either we have no object or we have an object and it is a 3d object + return true; } -//! check if this reference has valid geometry -bool ReferenceEntry::isValid() const +//! check if this reference has valid geometry in the model +bool ReferenceEntry::hasGeometry() const { - TopoDS_Shape geom = getGeometry(); - if (geom.IsNull()) { + // Base::Console().Message("RE::hasGeometry()\n"); + if (!getObject()) { return false; } - return true; + + if ( getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) ) { + // 2d reference + auto dvp = static_cast(getObject()); + if (getSubName().empty()) { + return false; + } + int geomNumber = DU::getIndexFromName(getSubName()); + std::string gType = geomType(); + if (gType == "Vertex") { + auto vert = dvp->getProjVertexByIndex(geomNumber); + if (vert) { + return true; + } + } else if (gType == "Edge") { + auto edge = dvp->getGeomByIndex(geomNumber); + if (edge) { + return true; + } + } + // if we ever have dimensions for faces, add something here. + return false; + } + + // 3d reference + auto shape = Part::Feature::getTopoShape(getObject()); + auto subShape = shape.getSubShape(getSubName().c_str()); + if (!subShape.IsNull()) { + return true; + } + + return false; } diff --git a/src/Mod/TechDraw/App/DimensionReferences.h b/src/Mod/TechDraw/App/DimensionReferences.h index 28c239cd7df2..e9c41ed80e93 100644 --- a/src/Mod/TechDraw/App/DimensionReferences.h +++ b/src/Mod/TechDraw/App/DimensionReferences.h @@ -37,6 +37,7 @@ namespace App { class DocumentObject; +class Document; } namespace Part @@ -51,34 +52,39 @@ namespace TechDraw class TechDrawExport ReferenceEntry { public: - ReferenceEntry( App::DocumentObject* docObject, std::string subName ) { - setObject(docObject); - setSubName(subName); - } - ReferenceEntry(const ReferenceEntry& other) { - setObject(other.getObject()); - setSubName(other.getSubName()); - } + ReferenceEntry() {}; + ReferenceEntry( App::DocumentObject* docObject, std::string subName, App::Document* document = nullptr); + ReferenceEntry(const ReferenceEntry& other); ~ReferenceEntry() = default; + ReferenceEntry& operator= (const ReferenceEntry& otherRef); + App::DocumentObject* getObject() const; void setObject(App::DocumentObject* docObj) { m_object = docObj; } std::string getSubName(bool longForm = false) const; void setSubName(std::string subName) { m_subName = subName; } + std::string getObjectName() const { return m_objectName; } + void setObjectName(std::string name) { m_objectName = name; } + App::Document* getDocument() const { return m_document; } + void setDocument(App::Document* document) { m_document = document; } + TopoDS_Shape getGeometry() const; std::string geomType() const; bool isWholeObject() const; Part::TopoShape asTopoShape() const; - Part::TopoShape asTopoShapeVertex(TopoDS_Vertex &vert) const; - Part::TopoShape asTopoShapeEdge(TopoDS_Edge& edge) const; bool is3d() const; - bool isValid() const; + bool hasGeometry() const; private: - App::DocumentObject* m_object; - std::string m_subName; + Part::TopoShape asTopoShapeVertex(TopoDS_Vertex &vert) const; + Part::TopoShape asTopoShapeEdge(TopoDS_Edge& edge) const; + + App::DocumentObject* m_object{nullptr}; + std::string m_subName{""}; + std::string m_objectName{""}; + App::Document* m_document{nullptr}; }; using ReferenceVector = std::vector; diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index 6a2c692796bb..af497d6a1c03 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -23,33 +23,33 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include - -# include -# include -# include -# include -# include - -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif #include @@ -64,7 +64,7 @@ #include #include -#include // generated from DrawViewDimensionPy.xml +#include // generated from DrawViewDimensionPy.xml #include "DrawViewDimension.h" #include "DimensionFormatter.h" @@ -73,6 +73,7 @@ #include "Geometry.h" #include "GeometryMatcher.h" #include "Preferences.h" +#include "DimensionAutoCorrect.h" using namespace TechDraw; using namespace Part; @@ -84,70 +85,125 @@ using DU = DrawUtil; PROPERTY_SOURCE(TechDraw::DrawViewDimension, TechDraw::DrawView) -const char* DrawViewDimension::TypeEnums[] = {"Distance", "DistanceX", "DistanceY", - "DistanceZ", "Radius", "Diameter", - "Angle", "Angle3Pt", nullptr}; +const char* DrawViewDimension::TypeEnums[] = {"Distance", + "DistanceX", + "DistanceY", + "DistanceZ", + "Radius", + "Diameter", + "Angle", + "Angle3Pt", + nullptr}; const char* DrawViewDimension::MeasureTypeEnums[] = {"True", "Projected", nullptr}; // constraint to set the step size to 0.1 -static const App::PropertyQuantityConstraint::Constraints ToleranceConstraint = {-DBL_MAX, DBL_MAX, +static const App::PropertyQuantityConstraint::Constraints ToleranceConstraint = {-DBL_MAX, + DBL_MAX, 0.1}; // constraint to force positive values static const App::PropertyQuantityConstraint::Constraints PositiveConstraint = {0.0, DBL_MAX, 0.1}; DrawViewDimension::DrawViewDimension() { - //create the formatter since it will be needed to set default property values + // create the formatter since it will be needed to set default property values m_formatter = new DimensionFormatter(this); - ADD_PROPERTY_TYPE(References2D, (nullptr, nullptr), "", (App::Prop_None), + ADD_PROPERTY_TYPE(References2D, + (nullptr, nullptr), + "", + (App::Prop_None), "Projected Geometry References"); References2D.setScope(App::LinkScope::Global); - ADD_PROPERTY_TYPE(References3D, (nullptr, nullptr), "", (App::Prop_None), + ADD_PROPERTY_TYPE(References3D, + (nullptr, nullptr), + "", + (App::Prop_None), "3D Geometry References"); References3D.setScope(App::LinkScope::Global); - ADD_PROPERTY_TYPE(FormatSpec, (getDefaultFormatSpec()), "Format", App::Prop_Output, + ADD_PROPERTY_TYPE(FormatSpec, + (getDefaultFormatSpec()), + "Format", + App::Prop_Output, "Dimension format"); - ADD_PROPERTY_TYPE(FormatSpecOverTolerance, (getDefaultFormatSpec(true)), "Format", - App::Prop_Output, "Dimension overtolerance format"); - ADD_PROPERTY_TYPE(FormatSpecUnderTolerance, (getDefaultFormatSpec(true)), "Format", - App::Prop_Output, "Dimension undertolerance format"); + ADD_PROPERTY_TYPE(FormatSpecOverTolerance, + (getDefaultFormatSpec(true)), + "Format", + App::Prop_Output, + "Dimension overtolerance format"); + ADD_PROPERTY_TYPE(FormatSpecUnderTolerance, + (getDefaultFormatSpec(true)), + "Format", + App::Prop_Output, + "Dimension undertolerance format"); ADD_PROPERTY_TYPE(Arbitrary, (false), "Format", App::Prop_Output, "Value overridden by user"); - ADD_PROPERTY_TYPE(ArbitraryTolerances, (false), "Format", App::Prop_Output, + ADD_PROPERTY_TYPE(ArbitraryTolerances, + (false), + "Format", + App::Prop_Output, "Tolerance values overridden by user"); - Type.setEnums(TypeEnums);//dimension type: length, radius etc + Type.setEnums(TypeEnums); // dimension type: length, radius etc ADD_PROPERTY(Type, ((long)0)); MeasureType.setEnums(MeasureTypeEnums); - ADD_PROPERTY(MeasureType, ((long)1));//Projected (or True) measurement - ADD_PROPERTY_TYPE(TheoreticalExact, (false), "", App::Prop_Output, + ADD_PROPERTY(MeasureType, ((long)1)); // Projected (or True) measurement + ADD_PROPERTY_TYPE(TheoreticalExact, + (false), + "", + App::Prop_Output, "If theoretical exact (basic) dimension"); - ADD_PROPERTY_TYPE(EqualTolerance, (true), "", App::Prop_Output, + ADD_PROPERTY_TYPE(EqualTolerance, + (true), + "", + App::Prop_Output, "If over- and undertolerance are equal"); - ADD_PROPERTY_TYPE(OverTolerance, (0.0), "", App::Prop_Output, + ADD_PROPERTY_TYPE(OverTolerance, + (0.0), + "", + App::Prop_Output, "Overtolerance value\nIf 'Equal Tolerance' is true this is also\nthe negated " "value for 'Under Tolerance'"); OverTolerance.setUnit(Base::Unit::Length); OverTolerance.setConstraints(&ToleranceConstraint); - ADD_PROPERTY_TYPE(UnderTolerance, (0.0), "", App::Prop_Output, + ADD_PROPERTY_TYPE(UnderTolerance, + (0.0), + "", + App::Prop_Output, "Undertolerance value\nIf 'Equal Tolerance' is true it will be replaced\nby " "negative value of 'Over Tolerance'"); UnderTolerance.setUnit(Base::Unit::Length); UnderTolerance.setConstraints(&ToleranceConstraint); - ADD_PROPERTY_TYPE(Inverted, (false), "", App::Prop_Output, + ADD_PROPERTY_TYPE(Inverted, + (false), + "", + App::Prop_Output, "The dimensional value is displayed inverted"); - ADD_PROPERTY_TYPE(AngleOverride, (false), "Override", App::Prop_Output, + ADD_PROPERTY_TYPE(AngleOverride, + (false), + "Override", + App::Prop_Output, "User specified angles"); ADD_PROPERTY_TYPE(LineAngle, (0.0), "Override", App::Prop_Output, "Dimension line angle"); ADD_PROPERTY_TYPE(ExtensionAngle, (0.0), "Override", App::Prop_Output, "Extension line angle"); - ADD_PROPERTY_TYPE(SavedGeometry, () ,"References",(App::PropertyType)(App::Prop_None),"Reference Geometry"); + ADD_PROPERTY_TYPE(SavedGeometry, + (), + "References", + (App::PropertyType)(App::Prop_None), + "Reference Geometry"); SavedGeometry.setOrderRelevant(true); + ADD_PROPERTY_TYPE( + BoxCorners, + (), + "References", + (App::Prop_None), + "Feature bounding box corners as of last reference update. Used by autocorrect"); + + // hide the DrawView properties that don't apply to Dimensions ScaleType.setStatus(App::Property::ReadOnly, true); ScaleType.setStatus(App::Property::Hidden, true); @@ -163,16 +219,18 @@ DrawViewDimension::DrawViewDimension() FormatSpecUnderTolerance.setStatus(App::Property::ReadOnly, true); measurement = new Measure::Measurement(); - //TODO: should have better initial datumLabel position than (0, 0) in the DVP?? something closer to the object being measured? + // TODO: should have better initial datumLabel position than (0, 0) in the DVP?? something + // closer to the object being measured? - //initialize the descriptive geometry. - //TODO: should this be more like DVP with a "geometry object"? + // initialize the descriptive geometry. + // TODO: should this be more like DVP with a "geometry object"? resetLinear(); resetAngular(); resetArc(); m_hasGeometry = false; m_matcher = new GeometryMatcher(this); m_referencesCorrect = true; + m_corrector = new DimensionAutoCorrect(this); } DrawViewDimension::~DrawViewDimension() @@ -181,6 +239,7 @@ DrawViewDimension::~DrawViewDimension() measurement = nullptr; delete m_formatter; delete m_matcher; + delete m_corrector; } void DrawViewDimension::resetLinear() @@ -211,8 +270,8 @@ void DrawViewDimension::resetArc() void DrawViewDimension::onChanged(const App::Property* prop) { if (prop == &References3D) { - //have to rebuild the Measurement object - clear3DMeasurements();//Measurement object + // have to rebuild the Measurement object + clear3DMeasurements(); // Measurement object if (!(References3D.getValues()).empty()) { setAll3DMeasurement(); } @@ -225,15 +284,16 @@ void DrawViewDimension::onChanged(const App::Property* prop) if (prop == &References2D) { updateSavedGeometry(); - } else if (prop == &References3D) { + } + else if (prop == &References3D) { // remove the old measurement object clear3DMeasurements(); if (!(References3D.getValues()).empty()) { // rebuild the Measurement object setAll3DMeasurement(); } - else if (MeasureType.isValue("True")) {//empty 3dRefs, but True - MeasureType.touch(); //run MeasureType logic for this case + else if (MeasureType.isValue("True")) { // empty 3dRefs, but True + MeasureType.touch(); // run MeasureType logic for this case } updateSavedGeometry(); } @@ -317,8 +377,8 @@ void DrawViewDimension::onChanged(const App::Property* prop) void DrawViewDimension::Restore(Base::XMLReader& reader) // Old drawings did not have the equal tolerance options. -// We cannot just introduce it as being set to true because that would e.g. destroy tolerances like +1-2 -// Therefore set it to false for existing documents +// We cannot just introduce it as being set to true because that would e.g. destroy tolerances like +// +1-2 Therefore set it to false for existing documents { EqualTolerance.setValue(false); DrawView::Restore(reader); @@ -337,7 +397,8 @@ void DrawViewDimension::onDocumentRestored() } } -void DrawViewDimension::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, +void DrawViewDimension::handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, App::Property* prop) { if (prop == &OverTolerance && strcmp(TypeName, "App::PropertyFloat") == 0) { @@ -354,7 +415,8 @@ void DrawViewDimension::handleChangedPropertyType(Base::XMLReader& reader, const TechDraw::DrawView::handleChangedPropertyType(reader, TypeName, prop); } - // Over/Undertolerance were further changed from App::PropertyQuantity to App::PropertyQuantityConstraint + // Over/Undertolerance were further changed from App::PropertyQuantity to + // App::PropertyQuantityConstraint if (prop == &OverTolerance && strcmp(TypeName, "App::PropertyQuantity") == 0) { App::PropertyQuantity OverToleranceProperty; // restore the PropertyQuantity to be able to set its value @@ -382,7 +444,7 @@ short DrawViewDimension::mustExecute() const App::DocumentObjectExecReturn* DrawViewDimension::execute() { -// Base::Console().Message("DVD::execute() - %s\n", getNameInDocument()); + // Base::Console().Message("DVD::execute() - %s\n", getNameInDocument()); if (!okToProceed()) { return App::DocumentObject::StdReturn; } @@ -391,25 +453,40 @@ App::DocumentObjectExecReturn* DrawViewDimension::execute() resetAngular(); resetArc(); - const std::vector savedGeometry = SavedGeometry.getValues(); - if (!savedGeometry.empty()) { - // we can only correct references if we have saved geometry for comparison - m_referencesCorrect = compareSavedGeometry(); - if (!m_referencesCorrect) { - m_referencesCorrect = fixExactMatch(); - if (!m_referencesCorrect) { - handleNoExactMatch(); - } + // check if geometry pointed to by references matches the saved version. If + // everything matches, we don't need to correct anything. + std::vector referenceState; + bool refsAreValid = m_corrector->referencesHaveValidGeometry(referenceState); + if (!refsAreValid) { + m_corrector->set3dObjectCache(m_3dObjectCache); + ReferenceVector repairedRefs; + refsAreValid = m_corrector->autocorrectReferences(referenceState, repairedRefs); + if (!refsAreValid) { + // references are broken and we can not fix them + Base::Console().Warning("Autocorrect failed to fix references for %s\n", + getNameInDocument()); + m_referencesCorrect = false; + return new App::DocumentObjectExecReturn("Autocorrect failed to fix broken references", + this); + } + if (repairedRefs.front().is3d()) { + setReferences3d(repairedRefs); + } + else { + setReferences2d(repairedRefs); } } + // references are good, we can proceed + m_referencesCorrect = true; + // is this check still relevant or is it replace by the autocorrect and + // validate methods? if (References3D.getValues().empty() && !checkReferences2D()) { - Base::Console().Warning("%s has invalid 2D References\n", - getNameInDocument()); + Base::Console().Warning("%s has invalid 2D References\n", getNameInDocument()); return new App::DocumentObjectExecReturn("Dimension object has invalid 2d references"); } - //we have either or both valid References3D and References2D + // we have either or both valid References3D and References2D ReferenceVector references = getEffectiveReferences(); if (Type.isValue("Distance") || Type.isValue("DistanceX") || Type.isValue("DistanceY")) { @@ -461,34 +538,38 @@ bool DrawViewDimension::okToProceed() return false; } DrawViewPart* dvp = getViewPart(); - if (!dvp) + if (!dvp) { return false; + } if (!has2DReferences() && !has3DReferences()) { - //no references, can't do anything + // no references, can't do anything return App::DocumentObject::StdReturn; } if (!getViewPart()->hasGeometry()) { - //can't do anything until Source has geometry + // can't do anything until Source has geometry return false; } return true; } -bool DrawViewDimension::isMultiValueSchema() const { return m_formatter->isMultiValueSchema(); } +bool DrawViewDimension::isMultiValueSchema() const +{ + return m_formatter->isMultiValueSchema(); +} -std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int partial, - bool isDim) +std::string +DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int partial, bool isDim) { return m_formatter->formatValue(value, qFormatSpec, partial, isDim); } bool DrawViewDimension::haveTolerance() { - //if a numeric tolerance is specified AND - //tolerances are NOT arbitrary + // if a numeric tolerance is specified AND + // tolerances are NOT arbitrary if ((!DrawUtil::fpCompare(OverTolerance.getValue(), 0.0) || !DrawUtil::fpCompare(UnderTolerance.getValue(), 0.0)) && !ArbitraryTolerances.getValue()) { @@ -519,20 +600,20 @@ QStringList DrawViewDimension::getPrefixSuffixSpec(QString fSpec) return m_formatter->getPrefixSuffixSpec(fSpec); } -//!NOTE: this returns the Dimension value in internal units (ie mm)!!!! +//! NOTE: this returns the Dimension value in internal units (ie mm)!!!! double DrawViewDimension::getDimValue() { // Base::Console().Message("DVD::getDimValue()\n"); double result = 0.0; if (!has2DReferences() && !has3DReferences()) { - //nothing to measure + // nothing to measure return result; } if (!getViewPart()) { return result; } - if (!getViewPart()->hasGeometry()) { //happens when loading saved document + if (!getViewPart()->hasGeometry()) { // happens when loading saved document return result; } @@ -555,7 +636,7 @@ double DrawViewDimension::getDimValue() else if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) { result = measurement->angle(); } - else {//tarfu + else { // tarfu throw Base::ValueError("getDimValue() - Unknown Dimension Type (3)"); } } @@ -582,14 +663,14 @@ double DrawViewDimension::getDimValue() else if (Type.isValue("Radius")) { arcPoints pts = m_arcPoints; result = - pts.radius / getViewPart()->getScale();//Projected BaseGeom is scaled for drawing + pts.radius / getViewPart()->getScale(); // Projected BaseGeom is scaled for drawing } else if (Type.isValue("Diameter")) { arcPoints pts = m_arcPoints; result = (pts.radius * 2.0) - / getViewPart()->getScale();//Projected BaseGeom is scaled for drawing + / getViewPart()->getScale(); // Projected BaseGeom is scaled for drawing } - else if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) {//same as case "Angle"? + else if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) { // same as case "Angle"? anglePoints pts = m_anglePoints; Base::Vector3d vertex = pts.vertex(); Base::Vector3d leg0 = pts.first() - vertex; @@ -613,13 +694,13 @@ double DrawViewDimension::getDimValue() pointPair DrawViewDimension::getPointsOneEdge(ReferenceVector references) { - // Base::Console().Message("DVD::getPointsOneEdge()\n"); + // Base::Console().Message("DVD::getPointsOneEdge()\n"); App::DocumentObject* refObject = references.front().getObject(); int iSubelement = DrawUtil::getIndexFromName(references.front().getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //TODO: Notify if not straight line Edge? - //this is a 2d object (a DVP + subelements) + // TODO: Notify if not straight line Edge? + // this is a 2d object (a DVP + subelements) TechDraw::BaseGeomPtr geom = getViewPart()->getGeomByIndex(iSubelement); if (!geom) { std::stringstream ssMessage; @@ -635,8 +716,8 @@ pointPair DrawViewDimension::getPointsOneEdge(ReferenceVector references) return {generic->points[0], generic->points[1]}; } - //this is a 3d object - //get the endpoints of the edge in the DVP's coordinates + // this is a 3d object + // get the endpoints of the edge in the DVP's coordinates Base::Vector3d edgeEnd0, edgeEnd1; TopoDS_Shape geometry = references.front().getGeometry(); if (geometry.IsNull() || geometry.ShapeType() != TopAbs_EDGE) { @@ -660,7 +741,7 @@ pointPair DrawViewDimension::getPointsTwoEdges(ReferenceVector references) int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //this is a 2d object (a DVP + subelements) + // this is a 2d object (a DVP + subelements) TechDraw::BaseGeomPtr geom0 = getViewPart()->getGeomByIndex(iSubelement0); TechDraw::BaseGeomPtr geom1 = getViewPart()->getGeomByIndex(iSubelement1); if (!geom0 || !geom1) { @@ -671,7 +752,7 @@ pointPair DrawViewDimension::getPointsTwoEdges(ReferenceVector references) return closestPoints(geom0->getOCCEdge(), geom1->getOCCEdge()); } - //this is a 3d object + // this is a 3d object TopoDS_Shape geometry0 = references.at(0).getGeometry(); TopoDS_Shape geometry1 = references.at(1).getGeometry(); if (geometry0.IsNull() || geometry1.IsNull() || geometry0.ShapeType() != TopAbs_EDGE @@ -693,7 +774,7 @@ pointPair DrawViewDimension::getPointsTwoVerts(ReferenceVector references) int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //this is a 2d object (a DVP + subelements) + // this is a 2d object (a DVP + subelements) TechDraw::VertexPtr v0 = getViewPart()->getProjVertexByIndex(iSubelement0); TechDraw::VertexPtr v1 = getViewPart()->getProjVertexByIndex(iSubelement1); if (!v0 || !v1) { @@ -705,7 +786,7 @@ pointPair DrawViewDimension::getPointsTwoVerts(ReferenceVector references) return {v0->point(), v1->point()}; } - //this is a 3d object + // this is a 3d object TopoDS_Shape geometry0 = references.at(0).getGeometry(); TopoDS_Shape geometry1 = references.at(1).getGeometry(); if (geometry0.IsNull() || geometry1.IsNull() || geometry0.ShapeType() != TopAbs_VERTEX @@ -731,7 +812,7 @@ pointPair DrawViewDimension::getPointsEdgeVert(ReferenceVector references) int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //this is a 2d object (a DVP + subelements) + // this is a 2d object (a DVP + subelements) TechDraw::BaseGeomPtr edge; TechDraw::VertexPtr vertex; if (DrawUtil::getGeomTypeFromName(references.at(0).getSubName()) == "Edge") { @@ -746,33 +827,29 @@ pointPair DrawViewDimension::getPointsEdgeVert(ReferenceVector references) throw Base::RuntimeError("Missing geometry for dimension (4)"); } - //get curve from edge - double start, end; // curve parameters + // get curve from edge + double start, end; // curve parameters const Handle(Geom_Surface) hplane = new Geom_Plane(gp_Ax3()); - auto const occCurve = BRep_Tool::CurveOnPlane(edge->getOCCEdge() - , hplane - , TopLoc_Location() - , start - , end); + auto const occCurve = + BRep_Tool::CurveOnPlane(edge->getOCCEdge(), hplane, TopLoc_Location(), start, end); auto const occPoint = gp_Pnt2d(vertex->x(), vertex->y()); - //project point on curve + // project point on curve auto projector = Geom2dAPI_ProjectPointOnCurve(occPoint, occCurve); if (projector.NbPoints() > 0) { - auto p1 = Base::Vector3d(vertex->x(), vertex->y(), 0.0); - auto p2 = Base::Vector3d(projector.NearestPoint().X() - , projector.NearestPoint().Y() - , 0.0); - pointPair result = pointPair(p1, p2); - result.setExtensionLine(closestPoints(edge->getOCCEdge(), vertex->getOCCVertex())); - return result; + auto p1 = Base::Vector3d(vertex->x(), vertex->y(), 0.0); + auto p2 = + Base::Vector3d(projector.NearestPoint().X(), projector.NearestPoint().Y(), 0.0); + pointPair result = pointPair(p1, p2); + result.setExtensionLine(closestPoints(edge->getOCCEdge(), vertex->getOCCVertex())); + return result; } else { - // unable to project - return closestPoints(edge->getOCCEdge(), vertex->getOCCVertex()); + // unable to project + return closestPoints(edge->getOCCEdge(), vertex->getOCCVertex()); } } - //this is a 3d object + // this is a 3d object TopoDS_Shape geometry0 = references.at(0).getGeometry(); TopoDS_Shape geometry1 = references.at(1).getGeometry(); if (geometry0.IsNull() || geometry1.IsNull() || geometry0.ShapeType() != TopAbs_VERTEX @@ -793,7 +870,7 @@ arcPoints DrawViewDimension::getArcParameters(ReferenceVector references) int iSubelement = DrawUtil::getIndexFromName(references.front().getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //this is a 2d object (a DVP + subelements) + // this is a 2d object (a DVP + subelements) TechDraw::BaseGeomPtr geom = getViewPart()->getGeomByIndex(iSubelement); if (!geom) { std::stringstream ssMessage; @@ -803,7 +880,7 @@ arcPoints DrawViewDimension::getArcParameters(ReferenceVector references) return arcPointsFromBaseGeom(getViewPart()->getGeomByIndex(iSubelement)); } - //this is a 3d reference + // this is a 3d reference TopoDS_Shape geometry = references.front().getGeometry(); if (geometry.IsNull() || geometry.ShapeType() != TopAbs_EDGE) { throw Base::RuntimeError("Geometry for dimension reference is null."); @@ -837,10 +914,10 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base) } else { pts.isArc = false; - pts.onCurve.first(pts.center - + Base::Vector3d(1, 0, 0) * circle->radius);//arbitrary point on edge + pts.onCurve.first( + pts.center + Base::Vector3d(1, 0, 0) * circle->radius); // arbitrary point on edge pts.onCurve.second( - pts.center + Base::Vector3d(-1, 0, 0) * circle->radius);//arbitrary point on edge + pts.center + Base::Vector3d(-1, 0, 0) * circle->radius); // arbitrary point on edge } } else if ((base && base->getGeomType() == TechDraw::GeomType::ELLIPSE) @@ -853,9 +930,10 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base) pts.center = Base::Vector3d(ellipse->center.x, ellipse->center.y, 0.0); pts.radius = rAvg; pts.isArc = false; - pts.onCurve.first(pts.center + Base::Vector3d(1, 0, 0) * rAvg);//arbitrary point on edge + pts.onCurve.first(pts.center + + Base::Vector3d(1, 0, 0) * rAvg); // arbitrary point on edge pts.onCurve.second(pts.center - + Base::Vector3d(-1, 0, 0) * rAvg);//arbitrary point on edge + + Base::Vector3d(-1, 0, 0) * rAvg); // arbitrary point on edge } else { TechDraw::AOEPtr aoe = std::static_pointer_cast(base); @@ -869,10 +947,11 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base) pts.arcEnds.second(Base::Vector3d(aoe->endPnt.x, aoe->endPnt.y, 0.0)); pts.midArc = Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0); pts.arcCW = aoe->cw; - pts.onCurve.first(Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0));//for radius - // pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * rAvg); //for diameter + pts.onCurve.first(Base::Vector3d(aoe->midPnt.x, aoe->midPnt.y, 0.0)); // for radius + // pts.onCurve.first(pts.center + Base::Vector3d(1, 0,0) * rAvg); //for + // diameter pts.onCurve.second(pts.center - + Base::Vector3d(-1, 0, 0) * rAvg);//arbitrary point on edge + + Base::Vector3d(-1, 0, 0) * rAvg); // arbitrary point on edge } } else if (base && base->getGeomType() == TechDraw::GeomType::BSPLINE) { @@ -881,7 +960,7 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base) bool arc; double rad; Base::Vector3d center; - //bool circ = + // bool circ = GeometryUtils::getCircleParms(spline->getOCCEdge(), rad, center, arc); pts.center = Base::Vector3d(center.x, center.y, 0.0); pts.radius = rad; @@ -895,14 +974,14 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base) } else { pts.onCurve.first(pts.center - + Base::Vector3d(1, 0, 0) * rad);//arbitrary point on edge + + Base::Vector3d(1, 0, 0) * rad); // arbitrary point on edge pts.onCurve.second(pts.center - + Base::Vector3d(-1, 0, 0) * rad);//arbitrary point on edge + + Base::Vector3d(-1, 0, 0) * rad); // arbitrary point on edge } } else { - //fubar - can't have non-circular spline as target of Diameter dimension, but this is already - //checked, so something has gone badly wrong. + // fubar - can't have non-circular spline as target of Diameter dimension, but this is + // already checked, so something has gone badly wrong. Base::Console().Error("%s: can not make a Circle from this BSpline edge\n", getNameInDocument()); throw Base::RuntimeError("Bad BSpline geometry for arc dimension"); @@ -939,7 +1018,7 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) pts.center = DrawUtil::toVector3d(circle.Location()); pts.radius = circle.Radius(); if (pts.isArc) { - //part of circle + // part of circle gp_Ax1 axis = circle.Axis(); gp_Vec startVec = DrawUtil::togp_Vec(pts.arcEnds.first() - pts.center); gp_Vec endVec = DrawUtil::togp_Vec(pts.arcEnds.second() - pts.center); @@ -947,11 +1026,11 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) pts.arcCW = (angle < 0.0); } else { - //full circle + // full circle pts.onCurve.first(pts.center - + Base::Vector3d(1, 0, 0) * pts.radius);//arbitrary point on edge + + Base::Vector3d(1, 0, 0) * pts.radius); // arbitrary point on edge pts.onCurve.second(pts.center - + Base::Vector3d(-1, 0, 0) * pts.radius);//arbitrary point on edge + + Base::Vector3d(-1, 0, 0) * pts.radius); // arbitrary point on edge } } else if (adapt.GetType() == GeomAbs_Ellipse) { @@ -959,7 +1038,7 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) pts.center = DrawUtil::toVector3d(ellipse.Location()); pts.radius = (ellipse.MajorRadius() + ellipse.MinorRadius()) / 2.0; if (pts.isArc) { - //part of ellipse + // part of ellipse gp_Ax1 axis = ellipse.Axis(); gp_Vec startVec = DrawUtil::togp_Vec(pts.arcEnds.first() - pts.center); gp_Vec endVec = DrawUtil::togp_Vec(pts.arcEnds.second() - pts.center); @@ -967,11 +1046,11 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) pts.arcCW = (angle < 0.0); } else { - //full ellipse + // full ellipse pts.onCurve.first(pts.center - + Base::Vector3d(1, 0, 0) * pts.radius);//arbitrary point on edge + + Base::Vector3d(1, 0, 0) * pts.radius); // arbitrary point on edge pts.onCurve.second(pts.center - + Base::Vector3d(-1, 0, 0) * pts.radius);//arbitrary point on edge + + Base::Vector3d(-1, 0, 0) * pts.radius); // arbitrary point on edge } } else if (adapt.GetType() == GeomAbs_BSplineCurve) { @@ -984,11 +1063,11 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) throw Base::RuntimeError("failed to get circle from bspline"); } gp_Circ circle = adapt.Circle(); - //TODO: same code as above. reuse opportunity. + // TODO: same code as above. reuse opportunity. pts.center = DrawUtil::toVector3d(circle.Location()); pts.radius = circle.Radius(); if (pts.isArc) { - //part of circle + // part of circle gp_Ax1 axis = circle.Axis(); gp_Vec startVec = DrawUtil::togp_Vec(pts.arcEnds.first() - pts.center); gp_Vec endVec = DrawUtil::togp_Vec(pts.arcEnds.second() - pts.center); @@ -996,11 +1075,11 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) pts.arcCW = (angle < 0.0); } else { - //full circle - pts.onCurve.first(pts.center - + Base::Vector3d(1, 0, 0) * pts.radius);//arbitrary point on edge + // full circle + pts.onCurve.first( + pts.center + Base::Vector3d(1, 0, 0) * pts.radius); // arbitrary point on edge pts.onCurve.second( - pts.center + Base::Vector3d(-1, 0, 0) * pts.radius);//arbitrary point on edge + pts.center + Base::Vector3d(-1, 0, 0) * pts.radius); // arbitrary point on edge } } else { @@ -1016,13 +1095,13 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge) anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references) { - //Base::Console().Message("DVD::getAnglePointsTwoEdges() - %s\n", getNameInDocument()); + // Base::Console().Message("DVD::getAnglePointsTwoEdges() - %s\n", getNameInDocument()); App::DocumentObject* refObject = references.front().getObject(); int iSubelement0 = DrawUtil::getIndexFromName(references.at(0).getSubName()); int iSubelement1 = DrawUtil::getIndexFromName(references.at(1).getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //this is a 2d object (a DVP + subelements) + // this is a 2d object (a DVP + subelements) TechDraw::BaseGeomPtr geom0 = getViewPart()->getGeomByIndex(iSubelement0); TechDraw::BaseGeomPtr geom1 = getViewPart()->getGeomByIndex(iSubelement1); if (!geom0 || !geom1) { @@ -1046,7 +1125,7 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references TechDraw::GenericPtr generic1 = std::static_pointer_cast(geom1); Base::Vector3d apex = generic0->apparentInter(generic1); Base::Vector3d farPoint0, farPoint1; - //pick the end of generic0 farthest from the apex + // pick the end of generic0 farthest from the apex if ((generic0->getStartPoint() - apex).Length() > (generic0->getEndPoint() - apex).Length()) { farPoint0 = generic0->getStartPoint(); @@ -1054,7 +1133,7 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references else { farPoint0 = generic0->getEndPoint(); } - //pick the end of generic1 farthest from the apex + // pick the end of generic1 farthest from the apex if ((generic1->getStartPoint() - apex).Length() > (generic1->getEndPoint() - apex).Length()) { farPoint1 = generic1->getStartPoint(); @@ -1065,24 +1144,26 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references Base::Vector3d leg0Dir = (generic0->getStartPoint() - generic0->getEndPoint()).Normalize(); Base::Vector3d leg1Dir = (generic1->getStartPoint() - generic1->getEndPoint()).Normalize(); if (DrawUtil::fpCompare(fabs(leg0Dir.Dot(leg1Dir)), 1.0)) { - //legs of the angle are parallel. + // legs of the angle are parallel. throw Base::RuntimeError("Can not make angle from parallel edges"); } - Base::Vector3d extenPoint0 = farPoint0;//extension line points + Base::Vector3d extenPoint0 = farPoint0; // extension line points Base::Vector3d extenPoint1 = farPoint1; if (DrawUtil::fpCompare(fabs(leg0Dir.Dot(leg1Dir)), 0.0)) { - //legs of angle are perpendicular farPoints will do + // legs of angle are perpendicular farPoints will do } else { - //legs of the angle are skew - //project farthest points onto opposite edge + // legs of the angle are skew + // project farthest points onto opposite edge Base::Vector3d projFar0OnLeg1 = farPoint0.Perpendicular(apex, leg1Dir); Base::Vector3d projFar1OnLeg0 = farPoint1.Perpendicular(apex, leg0Dir); - if (DrawUtil::isBetween(projFar0OnLeg1, generic1->getStartPoint(), + if (DrawUtil::isBetween(projFar0OnLeg1, + generic1->getStartPoint(), generic1->getEndPoint())) { extenPoint1 = projFar0OnLeg1; } - else if (DrawUtil::isBetween(projFar1OnLeg0, generic0->getStartPoint(), + else if (DrawUtil::isBetween(projFar1OnLeg0, + generic0->getStartPoint(), generic0->getEndPoint())) { extenPoint0 = projFar1OnLeg0; } @@ -1095,7 +1176,7 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references return pts; } - //this is a 3d object + // this is a 3d object TopoDS_Shape geometry0 = references.at(0).getGeometry(); TopoDS_Shape geometry1 = references.at(1).getGeometry(); if (geometry0.IsNull() || geometry1.IsNull() || geometry0.ShapeType() != TopAbs_EDGE @@ -1117,9 +1198,11 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references gp_Pnt gEnd1 = BRep_Tool::Pnt(TopExp::LastVertex(edge1)); gp_Vec gDir1(gEnd1.XYZ() - gStart1.XYZ()); Base::Vector3d vApex; - bool haveIntersection = DrawUtil::intersect2Lines3d( - DrawUtil::toVector3d(gStart0), DrawUtil::toVector3d(gDir0), DrawUtil::toVector3d(gStart1), - DrawUtil::toVector3d(gDir1), vApex); + bool haveIntersection = DrawUtil::intersect2Lines3d(DrawUtil::toVector3d(gStart0), + DrawUtil::toVector3d(gDir0), + DrawUtil::toVector3d(gStart1), + DrawUtil::toVector3d(gDir1), + vApex); if (!haveIntersection) { throw Base::RuntimeError("Geometry for 3d angle dimension does not intersect"); } @@ -1134,14 +1217,16 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references if (gStart1.Distance(gApex) > gEnd1.Distance(gApex)) { gFar1 = gStart1; } - anglePoints pts(DrawUtil::toVector3d(gApex), DrawUtil::toVector3d(gFar0), + anglePoints pts(DrawUtil::toVector3d(gApex), + DrawUtil::toVector3d(gFar0), DrawUtil::toVector3d(gFar1)); pts.move(getViewPart()->getCurrentCentroid()); pts.project(getViewPart()); return pts; } -//TODO: this makes assumptions about the order of references (p - v - p). is this checked somewhere? +// TODO: this makes assumptions about the order of references (p - v - p). is this checked +// somewhere? anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector references) { // Base::Console().Message("DVD::getAnglePointsThreeVerts() - %s\n", getNameInDocument()); @@ -1154,7 +1239,7 @@ anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector referenc int iSubelement2 = DrawUtil::getIndexFromName(references.at(2).getSubName()); if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId()) && !references.at(0).getSubName().empty()) { - //this is a 2d object (a DVP + subelements) + // this is a 2d object (a DVP + subelements) TechDraw::VertexPtr vert0 = getViewPart()->getProjVertexByIndex(iSubelement0); TechDraw::VertexPtr vert1 = getViewPart()->getProjVertexByIndex(iSubelement1); TechDraw::VertexPtr vert2 = getViewPart()->getProjVertexByIndex(iSubelement2); @@ -1165,7 +1250,7 @@ anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector referenc return pts; } - //this is a 3d object + // this is a 3d object TopoDS_Shape geometry0 = references.at(0).getGeometry(); TopoDS_Shape geometry1 = references.at(1).getGeometry(); TopoDS_Shape geometry2 = references.at(2).getGeometry(); @@ -1180,7 +1265,8 @@ anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector referenc gp_Pnt point1 = BRep_Tool::Pnt(vertex1); TopoDS_Vertex vertex2 = TopoDS::Vertex(geometry2); gp_Pnt point2 = BRep_Tool::Pnt(vertex2); - anglePoints pts(DrawUtil::toVector3d(point1), DrawUtil::toVector3d(point0), + anglePoints pts(DrawUtil::toVector3d(point1), + DrawUtil::toVector3d(point0), DrawUtil::toVector3d(point2)); pts.move(getViewPart()->getCurrentCentroid()); pts.project(getViewPart()); @@ -1195,79 +1281,50 @@ DrawViewPart* DrawViewDimension::getViewPart() const return dynamic_cast(References2D.getValues().at(0)); } -//return the references controlling this dimension. 3d references are used when available -//otherwise 2d references are returned. no checking is performed. Result is pairs of (object, subName) +// return the references controlling this dimension. 3d references are used when available +// otherwise 2d references are returned. no checking is performed. Result is pairs of (object, +// subName) ReferenceVector DrawViewDimension::getEffectiveReferences() const { -// Base::Console().Message("DVD::getEffectiveReferences()\n"); + // Base::Console().Message("DVD::getEffectiveReferences()\n"); const std::vector& objects3d = References3D.getValues(); const std::vector& subElements3d = References3D.getSubValues(); const std::vector& objects = References2D.getValues(); const std::vector& subElements = References2D.getSubValues(); ReferenceVector effectiveRefs; - if (!objects3d.empty()) { - //use 3d references by preference - int refCount = objects3d.size(); + + // note that 3d references can be destroyed without our notice if the object + // is deleted. + if (objects3d.empty()) { + // use 2d references + int refCount = objects.size(); for (int i = 0; i < refCount; i++) { - ReferenceEntry ref(objects3d.at(i), std::string(subElements3d.at(i))); - effectiveRefs.push_back(ref); + if (subElements.empty()) { + // the 3d references have likely been nulled out by an object + // deletion. + ReferenceEntry ref(objects.at(i), std::string()); + effectiveRefs.push_back(ref); + } + else { + // normal 2d reference + ReferenceEntry ref(objects.at(i), subElements.at(i)); + effectiveRefs.push_back(ref); + } } } else { - //use 2d references if necessary - int refCount = objects.size(); + // use 3d references + int refCount = objects3d.size(); for (int i = 0; i < refCount; i++) { - ReferenceEntry ref(objects.at(i), subElements.at(i)); + ReferenceEntry ref(objects3d.at(i), std::string(subElements3d.at(i))); effectiveRefs.push_back(ref); } } return effectiveRefs; } -//return the 2d references as a ReferenceVector -ReferenceVector DrawViewDimension::getReferences2d() const -{ - const std::vector& objects = References2D.getValues(); - const std::vector& subElements = References2D.getSubValues(); - ReferenceVector refs2d; - int refCount = objects.size(); - for (int i = 0; i < refCount; i++) { - ReferenceEntry ref(objects.at(i), subElements.at(i)); - refs2d.push_back(ref); - } - return refs2d; -} -//return the 3d references as a ReferenceVector -ReferenceVector DrawViewDimension::getReferences3d() const -{ - const std::vector& objects3d = References3D.getValues(); - const std::vector& subElements3d = References3D.getSubValues(); - ReferenceVector refs3d; - int refCount = objects3d.size(); - for (int i = 0; i < refCount; i++) { - ReferenceEntry ref(objects3d.at(i), subElements3d.at(i)); - refs3d.push_back(ref); - } - return refs3d; -} - -void DrawViewDimension::replaceReferenceSubElement2d(int iRef, std::string& newSubelement) -{ -// Base::Console().Message("DVD::replaceReferenceSubElement2d(%d, %s)\n", iRef, newSubelement.c_str()); - ReferenceVector refs = getReferences2d(); - refs.at(iRef).setSubName(newSubelement); - setReferences2d(refs); -} - -void DrawViewDimension::replaceReferenceSubElement3d(int iRef, std::string& newSubelement) -{ - ReferenceVector refs = getReferences3d(); - refs.at(iRef).setSubName(newSubelement); - setReferences3d(refs); -} - -//what configuration of references do we have - Vertex-Vertex, Edge-Vertex, Edge, ... +// what configuration of references do we have - Vertex-Vertex, Edge-Vertex, Edge, ... int DrawViewDimension::getRefType() const { if (isExtentDim()) { @@ -1277,17 +1334,17 @@ int DrawViewDimension::getRefType() const ReferenceVector refs = getEffectiveReferences(); std::vector subNames; - //std::vector subNames = getEffectiveSubNames(); //??? + // std::vector subNames = getEffectiveSubNames(); //??? for (auto& ref : refs) { if (ref.getSubName().empty()) { - //skip this one + // skip this one continue; } subNames.push_back(ref.getSubName()); } if (subNames.empty()) { - //something went wrong, there were no subNames. + // something went wrong, there were no subNames. Base::Console().Message("DVD::getRefType - %s - there are no subNames.\n", getNameInDocument()); return 0; @@ -1296,8 +1353,8 @@ int DrawViewDimension::getRefType() const return getRefTypeSubElements(subNames); } -//TODO: Gui/DimensionValidators.cpp has almost the same code -//decide what the reference configuration is by examining the names of the sub elements +// TODO: Gui/DimensionValidators.cpp has almost the same code +// decide what the reference configuration is by examining the names of the sub elements int DrawViewDimension::getRefTypeSubElements(const std::vector& subElements) { int refType = invalidRef; @@ -1342,12 +1399,12 @@ bool DrawViewDimension::checkReferences2D() const const std::vector& subElements = References2D.getSubValues(); if (subElements.empty()) { - //must have at least 1 null string entry to balance DVP + // must have at least 1 null string entry to balance DVP return false; } if (subElements.front().empty() && !References3D.getValues().empty()) { - //this is (probably) a dim with 3d refs + // this is (probably) a dim with 3d refs return true; } @@ -1374,10 +1431,28 @@ bool DrawViewDimension::checkReferences2D() const return true; } +//! detect the state where 3d references have been nulled out due to +//! object deletion and the reference will need to be rebuilt. +bool DrawViewDimension::hasBroken3dReferences() const +{ + const std::vector& objects3d = References3D.getValues(); + const std::vector& objects = References2D.getValues(); + const std::vector& subElements = References2D.getSubValues(); + + if (objects.size() == 1 && objects3d.empty() && subElements.empty()) { + // we have the reference to the View, but no 2d subelements or 3d objects + // this means that the 3d references have been nulled out due to + // object deletion and the reference will need to be rebuilt. + return true; + } + return false; +} + + void DrawViewDimension::updateSavedGeometry() { -// Base::Console().Message("DVD::updateSavedGeometry() - %s - savedGeometry: %d\n", -// getNameInDocument(), SavedGeometry.getValues().size()); + // Base::Console().Message("DVD::updateSavedGeometry() - %s - savedGeometry: %d\n", + // getNameInDocument(), SavedGeometry.getValues().size()); ReferenceVector references = getEffectiveReferences(); if (references.empty()) { // no references to save @@ -1385,7 +1460,7 @@ void DrawViewDimension::updateSavedGeometry() } std::vector newGeometry; const std::vector oldGeometry = SavedGeometry.getValues(); - //need to clean up old geometry objects here? + // need to clean up old geometry objects here? size_t iOldGeom(0); for (auto& entry : references) { @@ -1393,14 +1468,16 @@ void DrawViewDimension::updateSavedGeometry() // view only reference has no geometry. continue; } - if (entry.isValid()) { + if (entry.hasGeometry()) { newGeometry.push_back(entry.asTopoShape()); - } else { - // use old geometry entry? null shape? have to put something in the vector - // so SavedGeometry and references stay in sync. + } + else { + // use old geometry entry? null shape? have to put something in the vector + // so SavedGeometry and references stay in sync. if (iOldGeom < oldGeometry.size()) { newGeometry.push_back(oldGeometry.at(iOldGeom)); - } else { + } + else { newGeometry.push_back(Part::TopoShape()); } } @@ -1408,209 +1485,10 @@ void DrawViewDimension::updateSavedGeometry() } if (!newGeometry.empty()) { SavedGeometry.setValues(newGeometry); + saveFeatureBox(); } } -// routines related to detecting that references no longer point to the same geometry as -// when they were created. -// returns true if the saved geometry is the same as the current reference geometry -// returns false if the saved geometry is different from the the current reference geometry -bool DrawViewDimension::compareSavedGeometry() -{ -// Base::Console().Message("DVD::compareSavedGeometry() - isRestoring: %d\n", isRestoring()); - const std::vector savedGeometry = SavedGeometry.getValues(); - if (savedGeometry.empty()) { - // no saved geometry, so we have nothing to compare, so we don't know if there has been a change - // this should return false, since something != nothing -// Base::Console().("%s has no saved reference geometry!\n", getNameInDocument()); - return false; - } - - ReferenceVector references = getEffectiveReferences(); - std::vector referenceGeometry; - for (auto& entry : references) { - referenceGeometry.push_back(entry.asTopoShape()); - } - if (savedGeometry.size() != referenceGeometry.size()) { -// Base::Console().Message("DVD::compareSavedGeometry - geometry sizes have changed\n"); - return false; - } - int geometryCount = savedGeometry.size(); - int iGeom = 0; - for ( ; iGeom < geometryCount; iGeom++) { - if (savedGeometry.at(iGeom).getTypeId() != referenceGeometry.at(iGeom).getTypeId()) { -// Base::Console().Message("DVD::compareSavedGeometry - saved geometry (%d) has different type\n", iGeom); - return false; - } - } - //saved and reference geometry have same count and types - for (iGeom = 0; iGeom < geometryCount; iGeom++) { - Part::TopoShape temp = savedGeometry.at(iGeom); - if (!m_matcher->compareGeometry(temp, referenceGeometry.at(iGeom)) ) { -// Base::Console().Message("DVD::compareSavedGeometry - saved geometry (%d) does not match current geometry\n", iGeom); - return false; - } - } - - //free the reference geometry? - return true; -} - -// deal with the situation where references do not point to the same geometry as -// when they were created. -bool DrawViewDimension::fixExactMatch() -{ -// Base::Console().Message("DVD::fixExactMatch() - reference geometry has changed\n"); - if (!Preferences::autoCorrectDimRefs()) { - return false; - } - ReferenceVector references = getEffectiveReferences(); - if (references.empty()) { - // could not get refs, something is wrong! - return false; - } - - if (SavedGeometry.getValues().empty()) { - // there is no saved geometry, so we can't repair anything. - return false; - } - std::vector< std::pair > refsToFix2d; - std::vector< std::pair > refsToFix3d; - bool success(true); - size_t referenceCount = references.size(); - size_t iRef = 0; - for ( ; iRef < referenceCount; iRef++) { - std::string newReference(""); - TopoDS_Shape geomShape = references.at(iRef).getGeometry(); - if (geomShape.IsNull()) { -// Base::Console().Message("DVD::fixExactMatch - no geometry found for reference: %d\n", iRef); - return false; - } - if (references.at(iRef).is3d()) { - if (geomShape.ShapeType() == TopAbs_VERTEX) { - newReference = recoverChangedVertex3d(iRef); - } else { - newReference = recoverChangedEdge3d(iRef); - } - if (!newReference.empty()) { - std::pair toFix(iRef, newReference); - refsToFix3d.push_back(toFix); - } else { - Base::Console().Message("%s - no exact match for changed 3d reference: %d\n", getNameInDocument(), iRef); - success = false; - } - } else { - if (geomShape.ShapeType() == TopAbs_VERTEX) { - newReference = recoverChangedVertex2d(iRef); - } else { - newReference = recoverChangedEdge2d(iRef); - } - if (!newReference.empty()) { - std::pair toFix(iRef, newReference); - refsToFix2d.push_back(toFix); - } else { - Base::Console().Message("%s - no exact match for changed 2d reference: %d\n", getNameInDocument(), iRef); - success = false; - } - } - } - - for (auto& fix : refsToFix2d) { - replaceReferenceSubElement2d(fix.first, fix.second); - } - for (auto& fix : refsToFix3d) { - replaceReferenceSubElement3d(fix.first, fix.second); - } - - return success; -} - -// deal with situation where the current geometry does not match the saved geometry, -// but we did not find an exact match in the geometry pile -void DrawViewDimension::handleNoExactMatch() -{ -// Base::Console().Message("DVD::handleNoExactMatch()\n"); -// Base::Console().Message("%s - trying to match changed geometry - stage 2\n", getNameInDocument()); - // this is where we insert the clever logic to determine that the changed geometry - // actually still represents the "front top left" edge. - // after figuring out the new reference, save the geometry - // updateSavedGeometry(); - m_referencesCorrect = true; -} - -//find an edge in the view that matches the reference entry's type and characteristics -std::string DrawViewDimension::recoverChangedEdge2d(int iReference) -{ -// Base::Console().Message("DVD::recoverChangedEdge2d(ref: %d)\n", iReference); - double scale = getViewPart()->getScale(); - Part::TopoShape savedGeometryItem = SavedGeometry.getValues().at(iReference); - std::vector gEdges = getViewPart()->getEdgeGeometry(); - int iEdge = 0; - for (auto& edge : gEdges) { - Part::TopoShape temp = edge->asTopoShape(scale); - if (savedGeometryItem.getTypeId() != temp.getTypeId()) { - // if the typeIds don't match, we can not compare the geometry -// Base::Console().Message("DVD::recoverChangedEdge2d - types do not match\n"); - iEdge++; - continue; - } - bool isSame = m_matcher->compareGeometry(savedGeometryItem, temp); -// Base::Console().Message("DVD::recoverChangedEdge2d - iEdge: %d isSame: %d\n", iEdge, isSame); - if (isSame) { - return std::string("Edge") + std::to_string(iEdge); - } - iEdge++; - } - return std::string(""); -} - -std::string DrawViewDimension::recoverChangedVertex2d(int iReference) -{ -// Base::Console().Message("DVD::recoverChangedVertex2d(%d)\n", iReference); - double scale = getViewPart()->getScale(); - std::vector savedAll = SavedGeometry.getValues(); - if (savedAll.empty() || - iReference >= int(savedAll.size())) { - return std::string(); - } - Part::TopoShape savedGeometryItem = SavedGeometry.getValues().at(iReference); - std::vector gVertexAll = getViewPart()->getVertexGeometry(); - int iVertex = 0; - for (auto& vert : gVertexAll) { - Part::TopoShape temp = vert->asTopoShape(scale); - bool isSame = m_matcher->compareGeometry(savedGeometryItem, temp); - if (isSame) { - return std::string("Vertex") + std::to_string(iVertex); - } - iVertex++; - } - return std::string(""); -} - -std::string DrawViewDimension::recoverChangedEdge3d(int iReference) -{ -// Base::Console().Message("DVD::recoverChangedEdge3d(%d)\n", iReference); - Part::TopoShape savedGeometryItem = SavedGeometry.getValues().at(iReference); - ReferenceVector references = getEffectiveReferences(); - App::DocumentObject* searchObject = references.at(iReference).getObject(); - Part::TopoShape shape = Part::Feature::getTopoShape(searchObject); - App::GeoFeature* geoFeat = dynamic_cast(searchObject); - //does a feature in a body get the body's globalPlacement?? - if (geoFeat) { - shape.setPlacement(geoFeat->globalPlacement()); - } - //TODO: these TopoShapes will have to be released when we are finished with them - std::vector edgesAll = getEdges(shape); - int iEdge = 1; //note that edge numbering starts at 1! - for (auto& edge : edgesAll) { - bool isSame = m_matcher->compareGeometry(savedGeometryItem, edge); - if (isSame) { - return std::string("Edge") + std::to_string(iEdge); - } - iEdge++; - } - return std::string(""); -} // based on Part::TopoShapePyImp::getShapes. Produces a vector of unique edges within the shape std::vector DrawViewDimension::getEdges(const TopoShape& inShape) @@ -1631,31 +1509,6 @@ std::vector DrawViewDimension::getEdges(const TopoShape& inShape) return ret; } -// as recoverChangedVertex2d, but 3d references do not need to be unscaled -std::string DrawViewDimension::recoverChangedVertex3d(int iReference) -{ -// Base::Console().Message("DVD::recoverChangedVertex3d(%d)\n", iReference); - Part::TopoShape savedGeometryItem = SavedGeometry.getValues().at(iReference); - ReferenceVector references = getEffectiveReferences(); - App::DocumentObject* searchObject = references.at(iReference).getObject(); - Part::TopoShape shape = Part::Feature::getTopoShape(searchObject); - App::GeoFeature* geoFeat = dynamic_cast(searchObject); - if (geoFeat) { - shape.setPlacement(geoFeat->globalPlacement()); - } - - //TODO: these TopoShapes will have to be released when we are finished with them - std::vector vertsAll = getVertexes(shape); - int iVert = 1; //note that vertex numbering starts at 1! - for (auto& vert : vertsAll) { - bool isSame = m_matcher->compareGeometry(savedGeometryItem, vert); - if (isSame) { - return std::string("Vertex") + std::to_string(iVert); - } - iVert++; - } - return std::string(""); -} // based on Part::TopoShapePyImp::getShapes std::vector DrawViewDimension::getVertexes(const TopoShape& inShape) @@ -1689,15 +1542,15 @@ pointPair DrawViewDimension::closestPoints(TopoDS_Shape s1, TopoDS_Shape s2) con result.first(Base::Vector3d(p.X(), p.Y(), p.Z())); p = extss.PointOnShape2(1); result.second(Base::Vector3d(p.X(), p.Y(), p.Z())); - }//TODO: else { explode } + } // TODO: else { explode } return result; } -//set the reference property from a reference vector +// set the reference property from a reference vector void DrawViewDimension::setReferences2d(ReferenceVector refs) { -// Base::Console().Message("DVD::setReferences2d(%d)\n", refs.size()); + // Base::Console().Message("DVD::setReferences2d(%d)\n", refs.size()); std::vector objects; std::vector subNames; if (objects.size() != subNames.size()) { @@ -1712,11 +1565,12 @@ void DrawViewDimension::setReferences2d(ReferenceVector refs) References2D.setValues(objects, subNames); } -//set the reference property from a reference vector +// set the reference property from a reference vector void DrawViewDimension::setReferences3d(ReferenceVector refs) { + // Base::Console().Message("DVD::setReferences3d()\n"); if (refs.empty() && !References3D.getValues().empty()) { - //clear the property of any old links + // clear the property of any old links References3D.setValue(nullptr, nullptr); return; } @@ -1729,12 +1583,23 @@ void DrawViewDimension::setReferences3d(ReferenceVector refs) for (size_t iRef = 0; iRef < refs.size(); iRef++) { objects.push_back(refs.at(iRef).getObject()); subNames.push_back(refs.at(iRef).getSubName()); + // cache the referenced object + m_3dObjectCache.insert(refs.at(iRef).getObject()->getNameInDocument()); + // cache the parent object if available. Ideally, we would handle deletion + // of a reference object in a slot for DocumentObject::signalDeletedObject, + // but by the time we get the signal the document will have severed any links + // between our object and its parents. So we need to cache the parent here while + // we still have the link + App::DocumentObject* firstParent = refs.at(iRef).getObject()->getFirstParent(); + if (firstParent) { + m_3dObjectCache.insert(firstParent->getNameInDocument()); + } } References3D.setValues(objects, subNames); } -//!add Dimension 3D references to measurement +//! add Dimension 3D references to measurement void DrawViewDimension::setAll3DMeasurement() { // Base::Console().Message("DVD::setAll3dMeasurement()\n"); @@ -1745,13 +1610,24 @@ void DrawViewDimension::setAll3DMeasurement() int i = 0; for (; i < end; i++) { static_cast(measurement->addReference3D(Objs.at(i), Subs.at(i))); + // cache the referenced object + m_3dObjectCache.insert(Objs.at(i)->getNameInDocument()); + // cache the parent object if available. Ideally, we would handle deletion + // of a reference object in a slot for DocumentObject::signalDeletedObject, + // but by the time we get the signal the document will have severed any links + // between our object and its parents. So we need to cache the parent here while + // we still have the link + App::DocumentObject* firstParent = Objs.at(i)->getFirstParent(); + if (firstParent) { + m_3dObjectCache.insert(firstParent->getNameInDocument()); + } } } -//delete all previous measurements +// delete all previous measurements void DrawViewDimension::clear3DMeasurements() { - //set sublinklist to empty? + // set sublinklist to empty? measurement->clear(); } @@ -1764,12 +1640,16 @@ void DrawViewDimension::dumpRefs2D(const char* text) const std::vector::const_iterator subIt = subElements.begin(); int i = 0; for (; objIt != objects.end(); objIt++, subIt++, i++) { - Base::Console().Message("DUMP - ref: %d object: %s subElement: %s\n", i, - (*objIt)->getNameInDocument(), (*subIt).c_str()); + Base::Console().Message("DUMP - ref: %d object: %s subElement: %s\n", + i, + (*objIt)->getNameInDocument(), + (*subIt).c_str()); } } -double DrawViewDimension::dist2Segs(Base::Vector3d s1, Base::Vector3d e1, Base::Vector3d s2, +double DrawViewDimension::dist2Segs(Base::Vector3d s1, + Base::Vector3d e1, + Base::Vector3d s2, Base::Vector3d e2) const { gp_Pnt start(s1.x, s1.y, 0.0); @@ -1794,7 +1674,7 @@ double DrawViewDimension::dist2Segs(Base::Vector3d s1, Base::Vector3d e1, Base:: double minDist = 0.0; if (count != 0) { minDist = extss.Value(); - }//TODO: else { explode } + } // TODO: else { explode } return minDist; } @@ -1836,9 +1716,41 @@ void DrawViewDimension::saveArrowPositions(const Base::Vector2d positions[]) } } -//return position within parent view of dimension arrow heads/dimline endpoints -//note positions are in apparent coord (inverted y). -pointPair DrawViewDimension::getArrowPositions() { return m_arrowPositions; } +// return the 2d references as a ReferenceVector +ReferenceVector DrawViewDimension::getReferences2d() const +{ + const std::vector& objects = References2D.getValues(); + const std::vector& subElements = References2D.getSubValues(); + ReferenceVector refs2d; + int refCount = objects.size(); + for (int i = 0; i < refCount; i++) { + ReferenceEntry ref(objects.at(i), subElements.at(i)); + refs2d.push_back(ref); + } + return refs2d; +} + +// return the 3d references as a ReferenceVector +ReferenceVector DrawViewDimension::getReferences3d() const +{ + const std::vector& objects3d = References3D.getValues(); + const std::vector& subElements3d = References3D.getSubValues(); + ReferenceVector refs3d; + int refCount = objects3d.size(); + for (int i = 0; i < refCount; i++) { + ReferenceEntry ref(objects3d.at(i), subElements3d.at(i)); + refs3d.push_back(ref); + } + return refs3d; +} + + +// return position within parent view of dimension arrow heads/dimline endpoints +// note positions are in apparent coord (inverted y). +pointPair DrawViewDimension::getArrowPositions() +{ + return m_arrowPositions; +} bool DrawViewDimension::has2DReferences() const { @@ -1846,23 +1758,26 @@ bool DrawViewDimension::has2DReferences() const const std::vector& objects = References2D.getValues(); const std::vector& subNames = References2D.getSubValues(); if (objects.empty()) { - //we don't even have a DVP + // we don't even have a DVP return false; } if (subNames.front().empty()) { - //this is ok, as we must have a null string entry to balance DVP in first object position + // this is ok, as we must have a null string entry to balance DVP in first object position return true; } - //we have a reference to a DVP and at least 1 subName entry, so we have 2d references + // we have a reference to a DVP and at least 1 subName entry, so we have 2d references return true; } -//there is no special structure to 3d references, so anything > 0 is good -bool DrawViewDimension::has3DReferences() const { return (References3D.getSize() > 0); } +// there is no special structure to 3d references, so anything > 0 is good +bool DrawViewDimension::has3DReferences() const +{ + return (References3D.getSize() > 0); +} -//has arbitrary or nonzero tolerance +// has arbitrary or nonzero tolerance bool DrawViewDimension::hasOverUnderTolerance() const { if (ArbitraryTolerances.getValue() || !DrawUtil::fpCompare(OverTolerance.getValue(), 0.0) @@ -1877,7 +1792,10 @@ bool DrawViewDimension::showUnits() const return Preferences::getPreferenceGroup("Dimensions")->GetBool("ShowUnits", false); } -bool DrawViewDimension::useDecimals() const { return Preferences::useGlobalDecimals(); } +bool DrawViewDimension::useDecimals() const +{ + return Preferences::useGlobalDecimals(); +} std::string DrawViewDimension::getPrefixForDimType() const { @@ -1885,7 +1803,8 @@ std::string DrawViewDimension::getPrefixForDimType() const return "R"; } else if (Type.isValue("Diameter")) { - return std::string(Preferences::getPreferenceGroup("Dimensions")->GetASCII("DiameterSymbol", "\xe2\x8c\x80"));// Diameter symbol + return std::string(Preferences::getPreferenceGroup("Dimensions") + ->GetASCII("DiameterSymbol", "\xe2\x8c\x80")); // Diameter symbol } return ""; @@ -1914,3 +1833,34 @@ PyObject* DrawViewDimension::getPyObject() } return Py::new_reference_to(PythonObject); } + +void DrawViewDimension::saveFeatureBox() +{ + std::vector bbxCorners; + auto bbx = getFeatureBox(); + bbxCorners.push_back(bbx.GetMinimum()); + bbxCorners.push_back(bbx.GetMaximum()); + BoxCorners.setValues(bbxCorners); +} + +Base::BoundBox3d DrawViewDimension::getSavedBox() +{ + std::vector bbxCorners = BoxCorners.getValues(); + if (bbxCorners.empty()) { + // need to advise caller if BoxCorners not filled in yet. zero length + // diagonal? + Base::Console().Message("DVD::getSavedBox - no corners!\n"); + return Base::BoundBox3d(); + } + return Base::BoundBox3d(bbxCorners.front().x, + bbxCorners.front().y, + bbxCorners.front().z, + bbxCorners.back().x, + bbxCorners.back().y, + bbxCorners.back().z); +} + +Base::BoundBox3d DrawViewDimension::getFeatureBox() +{ + return getViewPart()->getBoundingBox(); +} diff --git a/src/Mod/TechDraw/App/DrawViewDimension.h b/src/Mod/TechDraw/App/DrawViewDimension.h index 665a7445b67c..67987b8beb0c 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.h +++ b/src/Mod/TechDraw/App/DrawViewDimension.h @@ -23,8 +23,6 @@ #ifndef TechDraw_DrawViewDimension_h_ #define TechDraw_DrawViewDimension_h_ -#include - #include #include #include @@ -39,7 +37,8 @@ class TopoDS_Shape; -namespace Measure { +namespace Measure +{ class Measurement; } namespace TechDraw @@ -47,60 +46,63 @@ namespace TechDraw class DrawViewPart; class DimensionFormatter; class GeometryMatcher; +class DimensionAutoCorrect; -class TechDrawExport DrawViewDimension : public TechDraw::DrawView +class TechDrawExport DrawViewDimension: public TechDraw::DrawView { PROPERTY_HEADER_WITH_OVERRIDE(TechDraw::DrawViewDimension); public: - -// keep this enum synchronized with TypeEnums -enum DimensionType { - Distance, - DistanceX, - DistanceY, - DistanceZ, - Radius, - Diameter, - Angle, - Angle3Pt -}; + // keep this enum synchronized with TypeEnums + enum DimensionType + { + Distance, + DistanceX, + DistanceY, + DistanceZ, + Radius, + Diameter, + Angle, + Angle3Pt + }; /// Constructor DrawViewDimension(); ~DrawViewDimension() override; - App::PropertyEnumeration MeasureType; //True/Projected - App::PropertyLinkSubList References2D; //Points to Projection SubFeatures - App::PropertyLinkSubList References3D; //Points to 3D Geometry SubFeatures - App::PropertyEnumeration Type; //DistanceX, DistanceY, Diameter, etc. - - App::PropertyBool TheoreticalExact; - App::PropertyBool Inverted; - App::PropertyString FormatSpec; - App::PropertyString FormatSpecOverTolerance; - App::PropertyString FormatSpecUnderTolerance; - App::PropertyBool Arbitrary; - App::PropertyBool ArbitraryTolerances; - App::PropertyBool EqualTolerance; + App::PropertyEnumeration MeasureType; // True/Projected + App::PropertyLinkSubList References2D; // Points to Projection SubFeatures + App::PropertyLinkSubList References3D; // Points to 3D Geometry SubFeatures + App::PropertyEnumeration Type; // DistanceX, DistanceY, Diameter, etc. + + App::PropertyBool TheoreticalExact; + App::PropertyBool Inverted; + App::PropertyString FormatSpec; + App::PropertyString FormatSpecOverTolerance; + App::PropertyString FormatSpecUnderTolerance; + App::PropertyBool Arbitrary; + App::PropertyBool ArbitraryTolerances; + App::PropertyBool EqualTolerance; App::PropertyQuantityConstraint OverTolerance; App::PropertyQuantityConstraint UnderTolerance; - App::PropertyBool AngleOverride; - App::PropertyAngle LineAngle; - App::PropertyAngle ExtensionAngle; + App::PropertyBool AngleOverride; + App::PropertyAngle LineAngle; + App::PropertyAngle ExtensionAngle; - Part::PropertyTopoShapeList SavedGeometry; + Part::PropertyTopoShapeList SavedGeometry; + App::PropertyVectorList BoxCorners; - enum RefType{ - invalidRef, - oneEdge, - twoEdge, - twoVertex, - vertexEdge, - threeVertex, - extent - }; + enum RefType + { + invalidRef, + oneEdge, + twoEdge, + twoVertex, + vertexEdge, + threeVertex, + extent + }; short mustExecute() const override; @@ -108,23 +110,20 @@ enum DimensionType { virtual bool has3DReferences() const; bool hasOverUnderTolerance() const; - /** @name methods override Feature */ - //@{ - /// recalculate the Feature - App::DocumentObjectExecReturn *execute() override; - //@} + App::DocumentObjectExecReturn* execute() override; - /// returns the type name of the ViewProvider - const char* getViewProviderName() const override { + const char* getViewProviderName() const override + { return "TechDrawGui::ViewProviderDimension"; } - //return PyObject as DrawViewDimensionPy - PyObject *getPyObject() override; + // return PyObject as DrawViewDimensionPy + PyObject* getPyObject() override; virtual std::string getFormattedToleranceValue(int partial); virtual std::pair getFormattedToleranceValues(int partial = 0); virtual std::string getFormattedDimensionValue(int partial = 0); - virtual std::string formatValue(qreal value, QString qFormatSpec, int partial = 0, bool isDim = true); + virtual std::string + formatValue(qreal value, QString qFormatSpec, int partial = 0, bool isDim = true); virtual bool haveTolerance(); @@ -132,24 +131,51 @@ enum DimensionType { QStringList getPrefixSuffixSpec(QString fSpec); virtual DrawViewPart* getViewPart() const; - QRectF getRect() const override { return {0, 0, 1, 1}; } //pretend dimensions always fit! - virtual int getRefType() const; //Vertex-Vertex, Edge, Edge-Edge - static int getRefTypeSubElements(const std::vector &); //Vertex-Vertex, Edge, Edge-Edge + QRectF getRect() const override + { + return {0, 0, 1, 1}; + } // pretend dimensions always fit! + virtual int getRefType() const; // Vertex-Vertex, Edge, Edge-Edge + static int + getRefTypeSubElements(const std::vector&); // Vertex-Vertex, Edge, Edge-Edge void setReferences2d(ReferenceVector refs); void setReferences3d(ReferenceVector refs); ReferenceVector getReferences2d() const; ReferenceVector getReferences3d() const; + bool hasGoodReferences() const + { + return m_referencesCorrect; + } void setAll3DMeasurement(); void clear3DMeasurements(); virtual bool checkReferences2D() const; - virtual pointPair getLinearPoints() const {return m_linearPoints; } - virtual void setLinearPoints(Base::Vector3d point0, Base::Vector3d point1) { m_linearPoints.first(point0); - m_linearPoints.second(point1); }; - virtual void setLinearPoints(pointPair newPair) { m_linearPoints = newPair; } - arcPoints getArcPoints() {return m_arcPoints; } - anglePoints getAnglePoints() {return m_anglePoints; } + bool hasBroken3dReferences() const; + + + virtual pointPair getLinearPoints() const + { + return m_linearPoints; + } + virtual void setLinearPoints(Base::Vector3d point0, Base::Vector3d point1) + { + m_linearPoints.first(point0); + m_linearPoints.second(point1); + }; + virtual void setLinearPoints(pointPair newPair) + { + m_linearPoints = newPair; + } + arcPoints getArcPoints() + { + return m_arcPoints; + } + anglePoints getAnglePoints() + { + return m_anglePoints; + } + bool leaderIntersectsArc(Base::Vector3d s, Base::Vector3d pointOnCircle); bool isMultiValueSchema() const; @@ -161,10 +187,27 @@ enum DimensionType { bool useDecimals() const; bool isExtentDim() const; virtual ReferenceVector getEffectiveReferences() const; - bool goodReferenceGeometry() const { return m_referencesCorrect; } + + GeometryMatcher* getMatcher() const + { + return m_matcher; + } + DimensionAutoCorrect* getCorrector() const + { + return m_corrector; + } + + // these should probably be static as they don't use the dimension at all + std::vector getEdges(const Part::TopoShape& inShape); + std::vector getVertexes(const Part::TopoShape& inShape); + + // autocorrect support methods + void saveFeatureBox(); + Base::BoundBox3d getSavedBox(); + Base::BoundBox3d getFeatureBox(); protected: - void handleChangedPropertyType(Base::XMLReader &, const char * , App::Property * ) override; + void handleChangedPropertyType(Base::XMLReader&, const char*, App::Property*) override; void Restore(Base::XMLReader& reader) override; void onChanged(const App::Property* prop) override; void onDocumentRestored() override; @@ -182,13 +225,10 @@ enum DimensionType { virtual anglePoints getAnglePointsTwoEdges(ReferenceVector references); virtual anglePoints getAnglePointsThreeVerts(ReferenceVector references); - Measure::Measurement *measurement; - double dist2Segs(Base::Vector3d s1, - Base::Vector3d e1, - Base::Vector3d s2, - Base::Vector3d e2) const; - pointPair closestPoints(TopoDS_Shape s1, - TopoDS_Shape s2) const; + Measure::Measurement* measurement; + double + dist2Segs(Base::Vector3d s1, Base::Vector3d e1, Base::Vector3d s2, Base::Vector3d e2) const; + pointPair closestPoints(TopoDS_Shape s1, TopoDS_Shape s2) const; void resetLinear(); void resetAngular(); @@ -196,36 +236,27 @@ enum DimensionType { bool okToProceed(); void updateSavedGeometry(); - bool compareSavedGeometry(); - bool fixExactMatch(); - void handleNoExactMatch(); - std::string recoverChangedEdge2d(int iReference); - std::string recoverChangedEdge3d(int iReference); - std::string recoverChangedVertex2d(int iReference); - std::string recoverChangedVertex3d(int iReference); - void replaceReferenceSubElement2d(int iRef, std::string &newSubelement); - void replaceReferenceSubElement3d(int iRef, std::string &newSubelement); - - std::vector getEdges(const Part::TopoShape& inShape); - std::vector getVertexes(const Part::TopoShape& inShape); private: static const char* TypeEnums[]; static const char* MeasureTypeEnums[]; void dumpRefs2D(const char* text) const; - //Dimension "geometry" - pointPair m_linearPoints; - pointPair m_arrowPositions; - arcPoints m_arcPoints; + // Dimension "geometry" + pointPair m_linearPoints; + pointPair m_arrowPositions; + arcPoints m_arcPoints; anglePoints m_anglePoints; - bool m_hasGeometry; + bool m_hasGeometry; friend class DimensionFormatter; DimensionFormatter* m_formatter; GeometryMatcher* m_matcher; + DimensionAutoCorrect* m_corrector; + + bool m_referencesCorrect {false}; - bool m_referencesCorrect; + std::set m_3dObjectCache; }; -} //namespace TechDraw +} // namespace TechDraw #endif diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index 67c3f150bccd..a8aec4423a6e 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -862,7 +862,7 @@ TechDraw::VertexPtr DrawViewPart::getProjVertexByIndex(int idx) const return nullptr; } if ((unsigned)idx >= geoms.size()) { - Base::Console().Error("DVP::getProjVertexByIndex(%d) - invalid index\n", idx); + Base::Console().Error("DVP::getProjVertexByIndex(%d) - invalid index - size: %d\n", idx); return nullptr; } return geoms.at(idx); diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp index 9aa51f5b1f36..e73a2faf1a0c 100644 --- a/src/Mod/TechDraw/App/Geometry.cpp +++ b/src/Mod/TechDraw/App/Geometry.cpp @@ -1716,3 +1716,21 @@ bool GeometryUtils::isLine(TopoDS_Edge occEdge) } return false; } + + +//! make a line Edge from BSpline Edge +TopoDS_Edge GeometryUtils::asLine(TopoDS_Edge occEdge) +{ + BRepAdaptor_Curve c(occEdge); + + // find the two ends + Handle(Geom_Curve) curve = c.Curve().Curve(); + double first = c.FirstParameter(); + double last = c.LastParameter(); + gp_Pnt start = c.Value(first); + gp_Pnt end = c.Value(last); + + TopoDS_Edge result = BRepBuilderAPI_MakeEdge(start, end); + return result; +} + diff --git a/src/Mod/TechDraw/App/Geometry.h b/src/Mod/TechDraw/App/Geometry.h index 6c23407c32cd..6243ec5086a4 100644 --- a/src/Mod/TechDraw/App/Geometry.h +++ b/src/Mod/TechDraw/App/Geometry.h @@ -446,6 +446,8 @@ class TechDrawExport GeometryUtils static bool getCircleParms(TopoDS_Edge occEdge, double& radius, Base::Vector3d& center, bool& isArc); static TopoDS_Edge asCircle(TopoDS_Edge occEdge, bool& arc); static bool isLine(TopoDS_Edge occEdge); + static TopoDS_Edge asLine(TopoDS_Edge occEdge); + }; } //end namespace TechDraw diff --git a/src/Mod/TechDraw/App/GeometryMatcher.cpp b/src/Mod/TechDraw/App/GeometryMatcher.cpp index 1f3237073ab1..f177b9489440 100644 --- a/src/Mod/TechDraw/App/GeometryMatcher.cpp +++ b/src/Mod/TechDraw/App/GeometryMatcher.cpp @@ -45,23 +45,29 @@ #include "GeometryMatcher.h" #include "DrawUtil.h" +#include "Preferences.h" using namespace TechDraw; using DU = DrawUtil; // a set of routines for comparing geometry for equality. -bool GeometryMatcher::compareGeometry(Part::TopoShape shape1, Part::TopoShape shape2) +bool GeometryMatcher::compareGeometry(Part::TopoShape shape1, Part::TopoShape shape2) { -// Base::Console().Message("GM::compareGeometry()\n"); + // Base::Console().Message("GM::compareGeometry()\n"); + if (!Preferences::useExactMatchOnDims()) { + return false; + } if (shape1.isNull() || shape2.isNull()) { -// Base::Console().Message("GM::compareGeometry - one or more TopoShapes are null\n"); + // Base::Console().Message("GM::compareGeometry - one or more TopoShapes are + // null\n"); return false; } TopoDS_Shape geom1 = shape1.getShape(); TopoDS_Shape geom2 = shape2.getShape(); if (geom1.IsNull() || geom2.IsNull()) { -// Base::Console().Message("GM::compareGeometry - one or more TopoDS_Shapes are null\n"); + // Base::Console().Message("GM::compareGeometry - one or more TopoDS_Shapes are + // null\n"); return false; } @@ -74,11 +80,11 @@ bool GeometryMatcher::compareGeometry(Part::TopoShape shape1, Part::TopoShape s return false; } -bool GeometryMatcher::comparePoints(TopoDS_Shape &shape1, TopoDS_Shape &shape2) +bool GeometryMatcher::comparePoints(TopoDS_Shape& shape1, TopoDS_Shape& shape2) { -// Base::Console().Message("GM::comparePoints()\n"); - if (shape1.ShapeType() != TopAbs_VERTEX || - shape2.ShapeType() != TopAbs_VERTEX) { + // Base::Console().Message("GM::comparePoints()\n"); + + if (shape1.ShapeType() != TopAbs_VERTEX || shape2.ShapeType() != TopAbs_VERTEX) { // can not compare these shapes return false; } @@ -92,50 +98,47 @@ bool GeometryMatcher::comparePoints(TopoDS_Shape &shape1, TopoDS_Shape &shape2) return false; } -bool GeometryMatcher::compareEdges(TopoDS_Shape &shape1, TopoDS_Shape &shape2) +bool GeometryMatcher::compareEdges(TopoDS_Shape& shape1, TopoDS_Shape& shape2) { -// Base::Console().Message("GM::compareEdges()\n"); - if (shape1.ShapeType() != TopAbs_EDGE || - shape2.ShapeType() != TopAbs_EDGE) { + // Base::Console().Message("GM::compareEdges()\n"); + if (shape1.ShapeType() != TopAbs_EDGE || shape2.ShapeType() != TopAbs_EDGE) { // can not compare these shapes -// Base::Console().Message("GM::compareEdges - shape is not an edge\n"); + // Base::Console().Message("GM::compareEdges - shape is not an edge\n"); return false; } TopoDS_Edge edge1 = TopoDS::Edge(shape1); TopoDS_Edge edge2 = TopoDS::Edge(shape2); - if (edge1.IsNull() || edge2.IsNull()) { -// Base::Console().Message("GM::compareEdges - an input edge is null\n"); + if (edge1.IsNull() || edge2.IsNull()) { + // Base::Console().Message("GM::compareEdges - an input edge is null\n"); return false; } BRepAdaptor_Curve adapt1(edge1); BRepAdaptor_Curve adapt2(edge2); - if (adapt1.GetType() == GeomAbs_Line && - adapt2.GetType() == GeomAbs_Line) { + if (adapt1.GetType() == GeomAbs_Line && adapt2.GetType() == GeomAbs_Line) { return compareLines(edge1, edge2); } - if (adapt1.GetType() == GeomAbs_Circle && - adapt2.GetType() == GeomAbs_Circle) { + if (adapt1.GetType() == GeomAbs_Circle && adapt2.GetType() == GeomAbs_Circle) { if (adapt1.IsClosed() && adapt2.IsClosed()) { return compareCircles(edge1, edge2); - } else { + } + else { return compareCircleArcs(edge1, edge2); } } - if (adapt1.GetType() == GeomAbs_Ellipse && - adapt2.GetType() == GeomAbs_Ellipse) { + if (adapt1.GetType() == GeomAbs_Ellipse && adapt2.GetType() == GeomAbs_Ellipse) { if (adapt1.IsClosed() && adapt2.IsClosed()) { return compareEllipses(edge1, edge2); - } else { + } + else { return compareEllipseArcs(edge1, edge2); } } - if (adapt1.GetType() == GeomAbs_BSplineCurve && - adapt2.GetType() == GeomAbs_BSplineCurve) { + if (adapt1.GetType() == GeomAbs_BSplineCurve && adapt2.GetType() == GeomAbs_BSplineCurve) { return compareBSplines(edge1, edge2); } @@ -143,33 +146,32 @@ bool GeometryMatcher::compareEdges(TopoDS_Shape &shape1, TopoDS_Shape &shape2) return compareDifferent(edge1, edge2); } -bool GeometryMatcher::compareLines(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareLines(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { -// Base::Console().Message("GM::compareLines()\n"); + // Base::Console().Message("GM::compareLines()\n"); // how does the edge that was NOT null in compareEdges become null here? // should not happen, but does! if (edge1.IsNull() || edge2.IsNull()) { -// Base::Console().Message("GM::compareLine - an input edge is null\n"); + // Base::Console().Message("GM::compareLine - an input edge is null\n"); return false; } auto start1 = DU::toVector3d(BRep_Tool::Pnt(TopExp::FirstVertex(edge1))); auto end1 = DU::toVector3d(BRep_Tool::Pnt(TopExp::LastVertex(edge1))); auto start2 = DU::toVector3d(BRep_Tool::Pnt(TopExp::FirstVertex(edge2))); auto end2 = DU::toVector3d(BRep_Tool::Pnt(TopExp::LastVertex(edge2))); - if (start1.IsEqual(start2, EWTOLERANCE) && - end1.IsEqual(end2, EWTOLERANCE)) { - //exact match + if (start1.IsEqual(start2, EWTOLERANCE) && end1.IsEqual(end2, EWTOLERANCE)) { + // exact match return true; } return false; } -bool GeometryMatcher::compareCircles(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareCircles(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { -// Base::Console().Message("GM::compareCircles()\n"); + // Base::Console().Message("GM::compareCircles()\n"); // how does the edge that was NOT null in compareEdges become null here? if (edge1.IsNull() || edge2.IsNull()) { -// Base::Console().Message("GM::compareCircles - an input edge is null\n"); + // Base::Console().Message("GM::compareCircles - an input edge is null\n"); return false; } @@ -181,19 +183,18 @@ bool GeometryMatcher::compareCircles(TopoDS_Edge &edge1, TopoDS_Edge &edge2) double radius2 = circle2.Radius(); auto center1 = DU::toVector3d(circle1.Location()); auto center2 = DU::toVector3d(circle2.Location()); - if (DU::fpCompare(radius1, radius2, EWTOLERANCE) && - center1.IsEqual(center2, EWTOLERANCE)) { - //exact match + if (DU::fpCompare(radius1, radius2, EWTOLERANCE) && center1.IsEqual(center2, EWTOLERANCE)) { + // exact match return true; - } + } return false; } -bool GeometryMatcher::compareEllipses(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareEllipses(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { // how does the edge that was NOT null in compareEdges become null here? if (edge1.IsNull() || edge2.IsNull()) { -// Base::Console().Message("GM::compareEllipses - an input edge is null\n"); + // Base::Console().Message("GM::compareEllipses - an input edge is null\n"); return false; } @@ -207,19 +208,18 @@ bool GeometryMatcher::compareEllipses(TopoDS_Edge &edge1, TopoDS_Edge &edge2) double minor2 = ellipse2.MinorRadius(); auto center1 = DU::toVector3d(ellipse1.Location()); auto center2 = DU::toVector3d(ellipse2.Location()); - if (DU::fpCompare(major1, major2, EWTOLERANCE) && - DU::fpCompare(minor1, minor2, EWTOLERANCE) && - center1.IsEqual(center2, EWTOLERANCE)) { + if (DU::fpCompare(major1, major2, EWTOLERANCE) && DU::fpCompare(minor1, minor2, EWTOLERANCE) + && center1.IsEqual(center2, EWTOLERANCE)) { // exact match return true; } return false; - } +} // for our purposes, only lines or circles masquerading as bsplines are of interest -bool GeometryMatcher::compareBSplines(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareBSplines(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { -// Base::Console().Message("GM::compareBSplines()\n"); + // Base::Console().Message("GM::compareBSplines()\n"); // how does the edge that was NOT null in compareEdges become null here? if (edge1.IsNull() || edge2.IsNull()) { Base::Console().Message("GM::compareBSplines - an input edge is null\n"); @@ -258,31 +258,31 @@ bool GeometryMatcher::compareBSplines(TopoDS_Edge &edge1, TopoDS_Edge &edge2) } // this is a weak comparison. we should also check center & radius? -bool GeometryMatcher::compareCircleArcs(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareCircleArcs(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { return compareEndPoints(edge1, edge2); } -bool GeometryMatcher::compareEllipseArcs(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareEllipseArcs(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { return compareEndPoints(edge1, edge2); - } +} // this is where we would try to match a bspline against a line or a circle. // not sure how successful this would be. For now, we just say it doesn't match -bool GeometryMatcher::compareDifferent(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareDifferent(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { -// Base::Console().Message("GM::compareDifferent()\n"); + // Base::Console().Message("GM::compareDifferent()\n"); BRepAdaptor_Curve adapt1(edge1); BRepAdaptor_Curve adapt2(edge2); return false; } -bool GeometryMatcher::compareEndPoints(TopoDS_Edge &edge1, TopoDS_Edge &edge2) +bool GeometryMatcher::compareEndPoints(TopoDS_Edge& edge1, TopoDS_Edge& edge2) { // how does the edge that was NOT null in compareEdges become null here? if (edge1.IsNull() || edge2.IsNull()) { -// Base::Console().Message("GM::compareLine - an input edge is null\n"); + // Base::Console().Message("GM::compareLine - an input edge is null\n"); return false; } @@ -301,9 +301,8 @@ bool GeometryMatcher::compareEndPoints(TopoDS_Edge &edge1, TopoDS_Edge &edge2) props2.SetParameter(pLast2); auto end2 = DU::toVector3d(props2.Value()); - if (begin1.IsEqual(begin2, EWTOLERANCE) && - end1.IsEqual(end2, EWTOLERANCE)) { - //exact match + if (begin1.IsEqual(begin2, EWTOLERANCE) && end1.IsEqual(end2, EWTOLERANCE)) { + // exact match return true; } return false; diff --git a/src/Mod/TechDraw/App/GeometryMatcher.h b/src/Mod/TechDraw/App/GeometryMatcher.h index f6b27d24a67d..ad81be80d404 100644 --- a/src/Mod/TechDraw/App/GeometryMatcher.h +++ b/src/Mod/TechDraw/App/GeometryMatcher.h @@ -33,17 +33,23 @@ namespace Part class TopoShape; } -namespace TechDraw { +namespace TechDraw +{ -class TechDrawExport GeometryMatcher { +class TechDrawExport GeometryMatcher +{ public: - GeometryMatcher() {} - explicit GeometryMatcher(DrawViewDimension* dim) { m_dimension = dim; } + GeometryMatcher() + {} + explicit GeometryMatcher(DrawViewDimension* dim) + { + m_dimension = dim; + } ~GeometryMatcher() = default; - bool compareGeometry(Part::TopoShape geom1, Part::TopoShape geom2); - bool comparePoints(TopoDS_Shape& shape1, TopoDS_Shape& shape2); - bool compareEdges(TopoDS_Shape& shape1, TopoDS_Shape& shape2); + bool compareGeometry(Part::TopoShape geom1, Part::TopoShape geom2); + bool comparePoints(TopoDS_Shape& shape1, TopoDS_Shape& shape2); + bool compareEdges(TopoDS_Shape& shape1, TopoDS_Shape& shape2); bool compareLines(TopoDS_Edge& edge1, TopoDS_Edge& edge2); bool compareCircles(TopoDS_Edge& edge1, TopoDS_Edge& edge2); @@ -53,12 +59,21 @@ class TechDrawExport GeometryMatcher { bool compareCircleArcs(TopoDS_Edge& edge1, TopoDS_Edge& edge2); bool compareEllipseArcs(TopoDS_Edge& edge1, TopoDS_Edge& edge2); + double getPointTolerance() const + { + return m_pointTolerance; + } + void setPointTolerance(double tol) + { + m_pointTolerance = tol; + } + private: bool compareEndPoints(TopoDS_Edge& edge1, TopoDS_Edge& edge2); DrawViewDimension* m_dimension; + double m_pointTolerance {EWTOLERANCE}; }; -} //end namespace TechDraw +} // end namespace TechDraw #endif - diff --git a/src/Mod/TechDraw/App/Preferences.cpp b/src/Mod/TechDraw/App/Preferences.cpp index 9546ac73ed25..ae35b68fa0d6 100644 --- a/src/Mod/TechDraw/App/Preferences.cpp +++ b/src/Mod/TechDraw/App/Preferences.cpp @@ -527,3 +527,13 @@ int Preferences::sectionLineConvention() { return getPreferenceGroup("Standards")->GetInt("SectionLineStandard", 1); } + + +//! true if the GeometryMatcher should be used in correcting Dimension references +bool Preferences::useExactMatchOnDims() +{ + return getPreferenceGroup("Dimensions")->GetBool("UseMatcher", true); +} + + + diff --git a/src/Mod/TechDraw/App/Preferences.h b/src/Mod/TechDraw/App/Preferences.h index 6387bc9ed7cb..d36636832b45 100644 --- a/src/Mod/TechDraw/App/Preferences.h +++ b/src/Mod/TechDraw/App/Preferences.h @@ -126,6 +126,8 @@ class TechDrawExport Preferences static std::string currentElementDefFile(); static int sectionLineConvention(); + + static bool useExactMatchOnDims(); }; diff --git a/src/Mod/TechDraw/Gui/Command.cpp b/src/Mod/TechDraw/Gui/Command.cpp index 53d0863695d1..f6ba42655b43 100644 --- a/src/Mod/TechDraw/Gui/Command.cpp +++ b/src/Mod/TechDraw/Gui/Command.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #endif @@ -75,7 +74,7 @@ #include "TaskProjection.h" #include "TaskSectionView.h" #include "ViewProviderPage.h" -#include "ViewProviderViewPart.h" +#include "ViewProviderDrawingView.h" void execSimpleSection(Gui::Command* cmd); void execComplexSection(Gui::Command* cmd); diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 451217ed51a6..6a3e7d139dbe 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -630,10 +630,9 @@ void QGIViewDimension::updateView(bool update) updateDim(); } - if (dim->goodReferenceGeometry()) { + if (dim->hasGoodReferences()) { m_refFlag->hide(); } else { -// m_refFlag->setPos(datumLabel->pos()); m_refFlag->centerAt(datumLabel->pos() + datumLabel->boundingRect().center()); m_refFlag->show(); }