* [Meas]Changes for TD dimension refs for links * [TD]App changes for dim refs to links * [TD]Gui changes for dim refs to links * [TD]fix 2 lint messages * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
170
src/Mod/Measure/App/AppMeasurePy.cpp
Normal file
170
src/Mod/Measure/App/AppMeasurePy.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 wandererfan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
|
||||
#endif
|
||||
#include <Mod/Measure/MeasureGlobal.h>
|
||||
|
||||
#include <algorithm> // clears "include what you use" lint message, but creates "included header not used"
|
||||
#include <string>
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/MatrixPy.h>
|
||||
#include <Base/PlacementPy.h>
|
||||
#include <Base/PyWrapParseTupleAndKeywords.h>
|
||||
|
||||
#include <Mod/Part/App/TopoShapePy.h>
|
||||
#include "Mod/Part/App/OCCError.h"
|
||||
|
||||
#include "ShapeFinder.h"
|
||||
|
||||
|
||||
namespace Measure
|
||||
{
|
||||
// module level static C++ functions go here
|
||||
}
|
||||
|
||||
namespace Measure
|
||||
{
|
||||
/** Copies a Python dictionary of Python strings to a C++ container.
|
||||
*
|
||||
* After the function call, the key-value pairs of the Python
|
||||
* dictionary are copied into the target buffer as C++ pairs
|
||||
* (pair<string, string>).
|
||||
*
|
||||
* @param sourceRange is a Python dictionary (Py::Dict). Both, the
|
||||
* keys and the values must be Python strings.
|
||||
*
|
||||
* @param targetIt refers to where the data should be inserted. Must
|
||||
* be of concept output iterator.
|
||||
*/
|
||||
template<typename OutputIt>
|
||||
void copy(Py::Dict sourceRange, OutputIt targetIt)
|
||||
{
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
for (const auto& keyPy : sourceRange.keys()) {
|
||||
key = Py::String(keyPy);
|
||||
value = Py::String(sourceRange[keyPy]);
|
||||
*targetIt = {key, value};
|
||||
++targetIt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Module: public Py::ExtensionModule<Module>
|
||||
{
|
||||
public:
|
||||
Module()
|
||||
: Py::ExtensionModule<Module>("Measure")
|
||||
{
|
||||
add_varargs_method(
|
||||
"getLocatedTopoShape",
|
||||
&Module::getLocatedTopoShape,
|
||||
"Part.TopoShape = Measure.getLocatedTopoShape(DocumentObject, longSubElement) Resolves "
|
||||
"the net placement of DocumentObject and returns the object's shape/subshape with the "
|
||||
"net placement applied. Link scaling operations along the path are also applied.");
|
||||
initialize("This is a module for measuring"); // register with Python
|
||||
}
|
||||
~Module() override
|
||||
{}
|
||||
|
||||
private:
|
||||
Py::Object invoke_method_varargs(void* method_def, const Py::Tuple& args) override
|
||||
{
|
||||
try {
|
||||
return Py::ExtensionModule<Module>::invoke_method_varargs(method_def, args);
|
||||
}
|
||||
catch (const Standard_Failure& e) {
|
||||
std::string str;
|
||||
Standard_CString msg = e.GetMessageString();
|
||||
str += typeid(e).name();
|
||||
str += " ";
|
||||
if (msg) {
|
||||
str += msg;
|
||||
}
|
||||
else {
|
||||
str += "No OCCT Exception Message";
|
||||
}
|
||||
Base::Console().Error("%s\n", str.c_str());
|
||||
throw Py::Exception(Part::PartExceptionOCCError, str);
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
std::string str;
|
||||
str += "FreeCAD exception thrown (";
|
||||
str += e.what();
|
||||
str += ")";
|
||||
e.ReportException();
|
||||
throw Py::RuntimeError(str);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::string str;
|
||||
str += "C++ exception thrown (";
|
||||
str += e.what();
|
||||
str += ")";
|
||||
Base::Console().Error("%s\n", str.c_str());
|
||||
throw Py::RuntimeError(str);
|
||||
}
|
||||
}
|
||||
|
||||
Py::Object getLocatedTopoShape(const Py::Tuple& args)
|
||||
{
|
||||
PyObject* pyRootObject {nullptr};
|
||||
PyObject* pyLeafSubName {nullptr};
|
||||
App::DocumentObject* rootObject {nullptr};
|
||||
std::string leafSub;
|
||||
if (!PyArg_ParseTuple(args.ptr(), "OO", &pyRootObject, &pyLeafSubName)) {
|
||||
throw Py::TypeError("expected (rootObject, subname");
|
||||
}
|
||||
|
||||
if (PyObject_TypeCheck(pyRootObject, &(App::DocumentObjectPy::Type))) {
|
||||
rootObject = static_cast<App::DocumentObjectPy*>(pyRootObject)->getDocumentObjectPtr();
|
||||
}
|
||||
|
||||
if (PyUnicode_Check(pyLeafSubName)) {
|
||||
leafSub = PyUnicode_AsUTF8(pyLeafSubName);
|
||||
}
|
||||
|
||||
if (!rootObject) {
|
||||
return Py::None();
|
||||
}
|
||||
|
||||
// this is on the stack
|
||||
auto temp = ShapeFinder::getLocatedShape(*rootObject, leafSub);
|
||||
// need new in here to make the twin object on the heap
|
||||
auto topoShapePy = new Part::TopoShapePy(new Part::TopoShape(temp));
|
||||
return Py::asObject(topoShapePy);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Measure
|
||||
@@ -31,6 +31,7 @@ SET(MeasureModule_SRCS
|
||||
PreCompiled.cpp
|
||||
PreCompiled.h
|
||||
AppMeasure.cpp
|
||||
AppMeasurePy.cpp
|
||||
|
||||
# original service routines
|
||||
Measurement.cpp
|
||||
@@ -54,6 +55,11 @@ SET(MeasureModule_SRCS
|
||||
|
||||
Preferences.cpp
|
||||
Preferences.h
|
||||
|
||||
ShapeFinder.cpp
|
||||
ShapeFinder.h
|
||||
SubnameHelper.cpp
|
||||
SubnameHelper.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP("Module" FILES ${MeasureModule_SRCS})
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
|
||||
#include "Measurement.h"
|
||||
#include "MeasurementPy.h"
|
||||
#include "ShapeFinder.h"
|
||||
|
||||
|
||||
using namespace Measure;
|
||||
@@ -278,43 +279,12 @@ MeasureType Measurement::getType()
|
||||
return measureType;
|
||||
}
|
||||
|
||||
TopoDS_Shape Measurement::getShape(App::DocumentObject* rootObj, const char* subName) const
|
||||
TopoDS_Shape Measurement::getShape(App::DocumentObject* obj, const char* subName) const
|
||||
{
|
||||
std::vector<std::string> names = Base::Tools::splitSubName(subName);
|
||||
|
||||
if (names.empty()) {
|
||||
TopoDS_Shape shape = Part::Feature::getShape(rootObj);
|
||||
if (shape.IsNull()) {
|
||||
throw Part::NullShapeException("null shape in measurement");
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
try {
|
||||
App::DocumentObject* obj = rootObj->getSubObject(subName);
|
||||
|
||||
Part::TopoShape partShape = Part::Feature::getTopoShape(obj);
|
||||
|
||||
partShape.setPlacement(App::GeoFeature::getGlobalPlacement(obj, rootObj, subName));
|
||||
|
||||
TopoDS_Shape shape = partShape.getSubShape(names.back().c_str());
|
||||
if (shape.IsNull()) {
|
||||
throw Part::NullShapeException("null shape in measurement");
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
catch (const Base::Exception&) {
|
||||
// re-throw original exception
|
||||
throw;
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
throw Base::CADKernelError(e.GetMessageString());
|
||||
}
|
||||
catch (...) {
|
||||
throw Base::RuntimeError("Measurement: Unknown error retrieving shape");
|
||||
}
|
||||
return ShapeFinder::getLocatedShape(*obj, subName);
|
||||
}
|
||||
|
||||
|
||||
// TODO:: add lengthX, lengthY (and lengthZ??) support
|
||||
// Methods for distances (edge length, two points, edge and a point
|
||||
double Measurement::length() const
|
||||
|
||||
416
src/Mod/Measure/App/ShapeFinder.cpp
Normal file
416
src/Mod/Measure/App/ShapeFinder.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 wandererfan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
//! ShapeFinder is a class to obtain the located shape pointed at by a DocumentObject and a
|
||||
//! "new-style" long subelement name. It hides the complexities of obtaining the correct object
|
||||
//! and its placement.
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include <boost_regex.hpp>
|
||||
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
#include <TopLoc_Location.hxx>
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObjectGroup.h>
|
||||
#include <App/Link.h>
|
||||
#include <App/GeoFeature.h>
|
||||
#include <App/GeoFeatureGroupExtension.h>
|
||||
#include <App/Part.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Mod/Part/App/AttachExtension.h>
|
||||
#include <Mod/Part/App/Attacher.h>
|
||||
|
||||
#include "ShapeFinder.h"
|
||||
|
||||
|
||||
using namespace Measure;
|
||||
|
||||
//! ResolveResult is a class to hold the result of resolving a selection into the actual target
|
||||
//! object and traditional subElement name (Vertex1).
|
||||
|
||||
ResolveResult::ResolveResult(const App::DocumentObject* realTarget,
|
||||
const std::string& shortSubName,
|
||||
const App::DocumentObject* targetParent)
|
||||
: m_target(App::SubObjectT(realTarget, shortSubName.c_str()))
|
||||
, m_targetParent(App::DocumentObjectT(targetParent))
|
||||
{}
|
||||
|
||||
App::DocumentObject& ResolveResult::getTarget() const
|
||||
{
|
||||
return *(m_target.getObject());
|
||||
}
|
||||
|
||||
std::string ResolveResult::getShortSub() const
|
||||
{
|
||||
return m_target.getSubName();
|
||||
}
|
||||
|
||||
App::DocumentObject& ResolveResult::getTargetParent() const
|
||||
{
|
||||
return *(m_targetParent.getObject());
|
||||
}
|
||||
|
||||
|
||||
//! returns the actual target object and subname pointed to by selectObj and selectLongSub (which
|
||||
//! is likely a result from getSelection or getSelectionEx)
|
||||
ResolveResult ShapeFinder::resolveSelection(const App::DocumentObject& selectObj,
|
||||
const std::string& selectLongSub)
|
||||
{
|
||||
App::DocumentObject* targetParent {nullptr};
|
||||
std::string childName {};
|
||||
const char* subElement {nullptr};
|
||||
App::DocumentObject* realTarget =
|
||||
selectObj.resolve(selectLongSub.c_str(), &targetParent, &childName, &subElement);
|
||||
auto shortSub = getLastTerm(selectLongSub);
|
||||
return {realTarget, shortSub, targetParent};
|
||||
}
|
||||
|
||||
|
||||
//! returns the shape of rootObject+leafSub. Any transforms from objects in the path from rootObject
|
||||
//! to leafSub are applied to the shape.
|
||||
//! leafSub is typically obtained from Selection as it provides the appropriate longSubname. The
|
||||
//! leaf sub string can also be constructed by walking the tree.
|
||||
// TODO: to truly locate the shape, we need to consider attachments - see
|
||||
// ShapeExtractor::getShapesFromXRoot()
|
||||
// and ShapeFinder::getLinkAttachParent()
|
||||
TopoDS_Shape ShapeFinder::getLocatedShape(const App::DocumentObject& rootObject,
|
||||
const std::string& leafSub)
|
||||
{
|
||||
auto resolved = resolveSelection(rootObject, leafSub);
|
||||
auto target = &resolved.getTarget();
|
||||
auto shortSub = resolved.getShortSub();
|
||||
if (!target) {
|
||||
return {};
|
||||
}
|
||||
|
||||
TopoDS_Shape shape = Part::Feature::getShape(target);
|
||||
if (isShapeReallyNull(shape)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto cleanSub = removeTnpInfo(leafSub);
|
||||
auto transform = getGlobalTransform(rootObject, cleanSub);
|
||||
|
||||
shape = transformShape(shape, transform.first, transform.second);
|
||||
Part::TopoShape tShape {shape};
|
||||
if (!shortSub.empty()) {
|
||||
return tShape.getSubTopoShape(shortSub.c_str()).getShape();
|
||||
}
|
||||
|
||||
return tShape.getShape();
|
||||
}
|
||||
|
||||
|
||||
//! convenient version of previous method
|
||||
Part::TopoShape ShapeFinder::getLocatedTopoShape(const App::DocumentObject& rootObject,
|
||||
const std::string& leafSub)
|
||||
{
|
||||
return {getLocatedShape(rootObject, leafSub)};
|
||||
}
|
||||
|
||||
|
||||
//! traverse the tree from leafSub up to rootObject, obtaining placements along the way. Note that
|
||||
//! the placements will need to be applied in the reverse order (ie top down) of what is delivered
|
||||
//! in plm stack. leafSub is a dot separated longSubName which DOES NOT include rootObject. the
|
||||
//! result does not include rootObject's transform.
|
||||
void ShapeFinder::crawlPlacementChain(std::vector<Base::Placement>& plmStack,
|
||||
std::vector<Base::Matrix4D>& scaleStack,
|
||||
const App::DocumentObject& rootObject,
|
||||
const std::string& leafSub)
|
||||
{
|
||||
auto currentSub = leafSub;
|
||||
std::string previousSub {};
|
||||
while (!currentSub.empty() && currentSub != previousSub) {
|
||||
auto resolved = resolveSelection(rootObject, currentSub);
|
||||
auto target = &resolved.getTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
auto currentPlacement = getPlacement(target);
|
||||
auto currentScale = getScale(target);
|
||||
if (!currentPlacement.isIdentity() || !currentScale.isUnity()) {
|
||||
plmStack.push_back(currentPlacement);
|
||||
scaleStack.push_back(currentScale);
|
||||
}
|
||||
previousSub = currentSub;
|
||||
currentSub = pruneLastTerm(currentSub);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//! return inShape with placement and scaler applied. If inShape contains any infinite subshapes
|
||||
//! (such as Datum planes), the infinite shapes will not be included in the result.
|
||||
TopoDS_Shape ShapeFinder::transformShape(TopoDS_Shape& inShape,
|
||||
const Base::Placement& placement,
|
||||
const Base::Matrix4D& scaler)
|
||||
{
|
||||
if (isShapeReallyNull(inShape)) {
|
||||
return {};
|
||||
}
|
||||
// we modify the parameter shape here. we don't claim to be const, but may be better to copy
|
||||
// the shape?
|
||||
Part::TopoShape tshape {inShape};
|
||||
if (tshape.isInfinite()) {
|
||||
inShape = stripInfiniteShapes(inShape);
|
||||
}
|
||||
|
||||
// copying the shape prevents "non-orthogonal GTrsf" errors in some versions
|
||||
// of OCC. Something to do with triangulation of shape??
|
||||
// it may be that incremental mesh would work here too.
|
||||
BRepBuilderAPI_Copy copier(inShape);
|
||||
tshape = Part::TopoShape(copier.Shape());
|
||||
if (tshape.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
tshape.transformGeometry(scaler);
|
||||
tshape.setPlacement(placement);
|
||||
|
||||
return tshape.getShape();
|
||||
}
|
||||
|
||||
|
||||
//! this getter should work for any object, not just links
|
||||
Base::Placement ShapeFinder::getPlacement(const App::DocumentObject* root)
|
||||
{
|
||||
auto namedProperty = root->getPropertyByName("Placement");
|
||||
auto placementProperty = dynamic_cast<App::PropertyPlacement*>(namedProperty);
|
||||
if (namedProperty && placementProperty) {
|
||||
return placementProperty->getValue();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
//! get root's scale property. If root is not a Link related object, then the identity matrrix will
|
||||
//! be returned.
|
||||
Base::Matrix4D ShapeFinder::getScale(const App::DocumentObject* root)
|
||||
{
|
||||
if (!isLinkLike(root)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Base::Matrix4D linkScale;
|
||||
auto namedProperty = root->getPropertyByName("ScaleVector");
|
||||
auto scaleVectorProperty = dynamic_cast<App::PropertyVector*>(namedProperty);
|
||||
if (scaleVectorProperty) {
|
||||
linkScale.scale(scaleVectorProperty->getValue());
|
||||
}
|
||||
return linkScale;
|
||||
}
|
||||
|
||||
|
||||
//! there isn't convenient common ancestor for the members of the Link family. We use
|
||||
//! isLinkLike(obj) instead of obj->isDerivedFrom<ConvenientCommonAncestor>(). Some links have
|
||||
//! proxy objects and will not be detected by isDerivedFrom().
|
||||
bool ShapeFinder::isLinkLike(const App::DocumentObject* obj)
|
||||
{
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj->isDerivedFrom<App::Link>() || obj->isDerivedFrom<App::LinkElement>()
|
||||
|| obj->isDerivedFrom<App::LinkGroup>()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto namedProperty = obj->getPropertyByName("LinkedObject");
|
||||
auto linkedObjectProperty = dynamic_cast<App::PropertyLink*>(namedProperty);
|
||||
if (linkedObjectProperty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
namedProperty = obj->getPropertyByName("ElementList");
|
||||
auto elementListProperty = dynamic_cast<App::PropertyLinkList*>(namedProperty);
|
||||
return elementListProperty != nullptr;
|
||||
}
|
||||
|
||||
|
||||
//! Infinite shapes can not be projected, so they need to be removed. inShape is usually a compound.
|
||||
//! Datum features (Axis, Plane and CS) are examples of infinite shapes.
|
||||
TopoDS_Shape ShapeFinder::stripInfiniteShapes(const TopoDS_Shape& inShape)
|
||||
{
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound comp;
|
||||
builder.MakeCompound(comp);
|
||||
|
||||
TopoDS_Iterator it(inShape);
|
||||
for (; it.More(); it.Next()) {
|
||||
TopoDS_Shape shape = it.Value();
|
||||
if (shape.ShapeType() < TopAbs_SOLID) {
|
||||
// look inside composite shapes
|
||||
shape = stripInfiniteShapes(shape);
|
||||
}
|
||||
else if (Part::TopoShape(shape).isInfinite()) {
|
||||
continue;
|
||||
}
|
||||
// simple shape & finite
|
||||
builder.Add(comp, shape);
|
||||
}
|
||||
|
||||
return {std::move(comp)};
|
||||
}
|
||||
|
||||
|
||||
//! check for shape is null or shape has no subshapes(vertex/edge/face/etc)
|
||||
//! this handles the case of an empty compound which is not IsNull, but has no
|
||||
//! content.
|
||||
// Note: the same code exists in TechDraw::ShapeUtils
|
||||
bool ShapeFinder::isShapeReallyNull(const TopoDS_Shape& shape)
|
||||
{
|
||||
// if the shape is null or it has no subshapes, then it is really null
|
||||
return shape.IsNull() || !TopoDS_Iterator(shape).More();
|
||||
}
|
||||
|
||||
|
||||
//! Returns the net transformation of a path from rootObject to leafSub. rootObject's transform
|
||||
//! is included in the result.
|
||||
std::pair<Base::Placement, Base::Matrix4D>
|
||||
ShapeFinder::getGlobalTransform(const App::DocumentObject& rootObject, const std::string& leafSub)
|
||||
{
|
||||
// we prune the last term if it is a vertex, edge or face
|
||||
std::string newSub = removeGeometryTerm(leafSub);
|
||||
|
||||
std::vector<Base::Placement> plmStack;
|
||||
std::vector<Base::Matrix4D> scaleStack;
|
||||
// get transforms below rootObject
|
||||
// Note: root object is provided by the caller and may or may not be a top level object
|
||||
crawlPlacementChain(plmStack, scaleStack, rootObject, newSub);
|
||||
|
||||
auto pathTransform = sumTransforms(plmStack, scaleStack);
|
||||
|
||||
// apply the placements in reverse order - top to bottom
|
||||
// should this be rootObject's local transform?
|
||||
auto rootTransform = getGlobalTransform(&rootObject);
|
||||
|
||||
auto netPlm = rootTransform.first * pathTransform.first;
|
||||
auto netScale = rootTransform.second * pathTransform.second;
|
||||
|
||||
return {netPlm, netScale};
|
||||
}
|
||||
|
||||
|
||||
//! trys to get the global position and scale for a object with no information about the path
|
||||
//! through the tree from a root to cursor object.
|
||||
std::pair<Base::Placement, Base::Matrix4D>
|
||||
ShapeFinder::getGlobalTransform(const App::DocumentObject* cursorObject)
|
||||
{
|
||||
if (!cursorObject) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Base::Placement netPlm;
|
||||
Base::Matrix4D netScale = getScale(cursorObject);
|
||||
|
||||
Base::Placement geoPlm;
|
||||
auto geoCursor = dynamic_cast<const App::GeoFeature*>(cursorObject);
|
||||
if (!isLinkLike(cursorObject) && geoCursor) {
|
||||
netPlm = geoCursor->globalPlacement();
|
||||
return {netPlm, netScale};
|
||||
}
|
||||
|
||||
netPlm = getPlacement(cursorObject);
|
||||
|
||||
return {netPlm, netScale};
|
||||
}
|
||||
|
||||
|
||||
//! combine a series of placement & scale transforms. The input stacks are expected in leaf to root
|
||||
//! order, but the result is in the expected root to leaf order.
|
||||
std::pair<Base::Placement, Base::Matrix4D>
|
||||
ShapeFinder::sumTransforms(const std::vector<Base::Placement>& plmStack,
|
||||
const std::vector<Base::Matrix4D>& scaleStack)
|
||||
{
|
||||
Base::Placement netPlm;
|
||||
Base::Matrix4D netScale;
|
||||
|
||||
auto itRevPlm = plmStack.rbegin();
|
||||
for (; itRevPlm != plmStack.rend(); itRevPlm++) {
|
||||
netPlm *= *itRevPlm;
|
||||
}
|
||||
auto itRevScale = scaleStack.rbegin();
|
||||
for (; itRevScale != scaleStack.rend(); itRevScale++) {
|
||||
netScale *= *itRevScale;
|
||||
}
|
||||
|
||||
return {netPlm, netScale};
|
||||
}
|
||||
|
||||
|
||||
//! get the parent to which attachObject is attached via Links (not regular Part::Attacher
|
||||
//! attachment)
|
||||
App::DocumentObject* ShapeFinder::getLinkAttachParent(const App::DocumentObject* attachedObject)
|
||||
{
|
||||
auto namedProperty = attachedObject->getPropertyByName("a1AttParent");
|
||||
auto attachProperty = dynamic_cast<App::PropertyLink*>(namedProperty);
|
||||
if (namedProperty && attachProperty) {
|
||||
return attachProperty->getValue();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
//! debugging routine that returns a string representation of a placement.
|
||||
// TODO: this should be in Base::Placement?
|
||||
std::string ShapeFinder::PlacementAsString(const Base::Placement& inPlacement)
|
||||
{
|
||||
auto position = inPlacement.getPosition();
|
||||
auto rotation = inPlacement.getRotation();
|
||||
Base::Vector3d axis;
|
||||
double angle {0.0};
|
||||
rotation.getValue(axis, angle);
|
||||
std::stringstream ss;
|
||||
ss << "pos: (" << position.x << ", " << position.y << ", " << position.z << ") axis: ("
|
||||
<< axis.x << ", " << axis.y << ", " << axis.z << ") angle: " << Base::toDegrees(angle);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
//! debug routine. return readable form of TopLoc_Location from OCC
|
||||
std::string ShapeFinder::LocationAsString(const TopLoc_Location& location)
|
||||
{
|
||||
auto position = Base::Vector3d {location.Transformation().TranslationPart().X(),
|
||||
location.Transformation().TranslationPart().Y(),
|
||||
location.Transformation().TranslationPart().Z()};
|
||||
gp_XYZ axisDir;
|
||||
double angle {0};
|
||||
auto isRotation = location.Transformation().GetRotation(axisDir, angle);
|
||||
Base::Vector3d axis {axisDir.X(), axisDir.Y(), axisDir.Z()};
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "isRotation: " << isRotation << " pos: (" << position.x << ", " << position.y << ", "
|
||||
<< position.z << ") axis: (" << axisDir.X() << ", " << axisDir.Y() << ", " << axisDir.Z()
|
||||
<< ") angle: " << Base::toDegrees(angle);
|
||||
return ss.str();
|
||||
}
|
||||
126
src/Mod/Measure/App/ShapeFinder.h
Normal file
126
src/Mod/Measure/App/ShapeFinder.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 wandererfan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MEASURE_SHAPEFINDER_H
|
||||
#define MEASURE_SHAPEFINDER_H
|
||||
|
||||
#include <Mod/Measure/MeasureGlobal.h>
|
||||
|
||||
#include <TopoDS_Shape.hxx>
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObserver.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Matrix.h>
|
||||
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
|
||||
#include "SubnameHelper.h"
|
||||
|
||||
namespace Measure
|
||||
{
|
||||
|
||||
//! a class to hold the result of resolving a selection into the actual target object
|
||||
//! and traditional subElement name (Vertex1)
|
||||
|
||||
class MeasureExport ResolveResult
|
||||
{
|
||||
public:
|
||||
ResolveResult();
|
||||
ResolveResult(const App::DocumentObject* realTarget,
|
||||
const std::string& shortSubName,
|
||||
const App::DocumentObject* targetParent);
|
||||
|
||||
App::DocumentObject& getTarget() const;
|
||||
std::string getShortSub() const;
|
||||
App::DocumentObject& getTargetParent() const;
|
||||
|
||||
private:
|
||||
App::SubObjectT m_target;
|
||||
App::DocumentObjectT m_targetParent;
|
||||
};
|
||||
|
||||
|
||||
//! a class to obtain the located shape pointed at by a selection
|
||||
class MeasureExport ShapeFinder: public SubnameHelper
|
||||
{
|
||||
public:
|
||||
static TopoDS_Shape getLocatedShape(const App::DocumentObject& rootObject,
|
||||
const std::string& leafSub);
|
||||
static Part::TopoShape getLocatedTopoShape(const App::DocumentObject& rootObject,
|
||||
const std::string& leafSub);
|
||||
|
||||
|
||||
static std::pair<Base::Placement, Base::Matrix4D>
|
||||
getGlobalTransform(const App::DocumentObject& rootObject, const std::string& leafSub);
|
||||
static std::pair<Base::Placement, Base::Matrix4D>
|
||||
getGlobalTransform(const App::DocumentObject* cursorObject);
|
||||
|
||||
static void crawlPlacementChain(std::vector<Base::Placement>& plmStack,
|
||||
std::vector<Base::Matrix4D>& scaleStack,
|
||||
const App::DocumentObject& rootObj,
|
||||
const std::string& leafSub);
|
||||
|
||||
static ResolveResult resolveSelection(const App::DocumentObject& selectObj,
|
||||
const std::string& selectLongSub);
|
||||
|
||||
static Base::Placement getPlacement(const App::DocumentObject* root);
|
||||
static Base::Matrix4D getScale(const App::DocumentObject* root);
|
||||
|
||||
static bool isLinkLike(const App::DocumentObject* obj);
|
||||
static std::string PlacementAsString(const Base::Placement& inPlacement);
|
||||
static std::string LocationAsString(const TopLoc_Location& location);
|
||||
|
||||
static TopoDS_Shape transformShape(TopoDS_Shape& inShape,
|
||||
const Base::Placement& placement,
|
||||
const Base::Matrix4D& scaler);
|
||||
static TopoDS_Shape stripInfiniteShapes(const TopoDS_Shape& inShape);
|
||||
static bool isShapeReallyNull(const TopoDS_Shape& shape);
|
||||
|
||||
static std::pair<Base::Placement, Base::Matrix4D>
|
||||
sumTransforms(const std::vector<Base::Placement>& plmStack,
|
||||
const std::vector<Base::Matrix4D>& scaleStack);
|
||||
static App::DocumentObject* getLinkAttachParent(const App::DocumentObject* attachedObject);
|
||||
static Base::Placement getAttachedPlacement(const App::DocumentObject* cursorObject);
|
||||
|
||||
static std::string getFullPath(const App::DocumentObject* object);
|
||||
static std::vector<App::DocumentObject*> getGeometryRootObjects(const App::Document* doc);
|
||||
static std::vector<std::list<App::DocumentObject*>>
|
||||
getGeometryPathsFromOutList(const App::DocumentObject* object);
|
||||
|
||||
|
||||
private:
|
||||
static bool ignoreModule(const std::string& moduleName);
|
||||
static bool ignoreObject(const App::DocumentObject* object);
|
||||
static bool ignoreLinkAttachedObject(const App::DocumentObject* object,
|
||||
const App::DocumentObject* inlistObject);
|
||||
static std::vector<App::DocumentObject*>
|
||||
tidyInList(const std::vector<App::DocumentObject*>& inlist);
|
||||
static std::vector<App::DocumentObject*>
|
||||
tidyInListAttachment(const App::DocumentObject* owner,
|
||||
const std::vector<App::DocumentObject*>& inlist);
|
||||
};
|
||||
|
||||
} // namespace Measure
|
||||
|
||||
#endif // MEASURE_SHAPEFINDER_H
|
||||
178
src/Mod/Measure/App/SubnameHelper.cpp
Normal file
178
src/Mod/Measure/App/SubnameHelper.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 wandererfan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
//! a class to perform common operations on subelement names.
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include <boost_regex.hpp>
|
||||
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include "SubnameHelper.h"
|
||||
|
||||
|
||||
using namespace Measure;
|
||||
|
||||
|
||||
std::string SubnameHelper::pathToLongSub(std::list<App::DocumentObject*> path)
|
||||
{
|
||||
std::vector<std::string> elementNames;
|
||||
for (auto& item : path) {
|
||||
auto name = item->getNameInDocument();
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
elementNames.emplace_back(name);
|
||||
}
|
||||
return namesToLongSub(elementNames);
|
||||
}
|
||||
|
||||
|
||||
//! construct dot separated long subelement name from a list of elements. the elements should be
|
||||
//! in topological order.
|
||||
std::string SubnameHelper::namesToLongSub(const std::vector<std::string>& pathElementNames)
|
||||
{
|
||||
std::string result;
|
||||
for (auto& name : pathElementNames) {
|
||||
result += (name + ".");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//! return the last term of a dot separated string - A.B.C returns C
|
||||
std::string SubnameHelper::getLastTerm(const std::string& inString)
|
||||
{
|
||||
auto result {inString};
|
||||
size_t lastDot = inString.rfind('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
result = result.substr(lastDot + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! return the first term of a dot separated string - A.B.C returns A
|
||||
std::string SubnameHelper::getFirstTerm(const std::string& inString)
|
||||
{
|
||||
auto result {inString};
|
||||
size_t lastDot = inString.find('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
result = result.substr(0, lastDot);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! remove the first term of a dot separated string - A.B.C returns B.C
|
||||
std::string SubnameHelper::pruneFirstTerm(const std::string& inString)
|
||||
{
|
||||
auto result {inString};
|
||||
size_t lastDot = inString.find('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
result = result.substr(lastDot + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! return a dot separated string without its last term - A.B.C returns A.B.
|
||||
// A.B.C. returns A.B.C
|
||||
std::string SubnameHelper::pruneLastTerm(const std::string& inString)
|
||||
{
|
||||
auto result {inString};
|
||||
if (result.back() == '.') {
|
||||
// remove the trailing dot
|
||||
result = result.substr(0, result.length() - 1);
|
||||
}
|
||||
|
||||
size_t lastDotPos = result.rfind('.');
|
||||
if (lastDotPos != std::string::npos) {
|
||||
result = result.substr(0, lastDotPos + 1);
|
||||
}
|
||||
else {
|
||||
// no dot in string, remove everything!
|
||||
result = "";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//! remove that part of a long subelement name that refers to a geometric subshape. "myObj.Vertex1"
|
||||
//! would return "myObj.", "myObj.mySubObj." would return itself unchanged. If there is no
|
||||
//! geometric reference the original input is returned.
|
||||
std::string SubnameHelper::removeGeometryTerm(const std::string& longSubname)
|
||||
{
|
||||
auto lastTerm = getLastTerm(longSubname);
|
||||
if (longSubname.empty() || longSubname.back() == '.') {
|
||||
// not a geometric reference
|
||||
return longSubname; // need a copy?
|
||||
}
|
||||
|
||||
// brute force check for geometry names in the last term
|
||||
auto pos = lastTerm.find("Vertex");
|
||||
if (pos != std::string::npos) {
|
||||
return pruneLastTerm(longSubname);
|
||||
}
|
||||
|
||||
pos = lastTerm.find("Edge");
|
||||
if (pos != std::string::npos) {
|
||||
return pruneLastTerm(longSubname);
|
||||
}
|
||||
|
||||
pos = lastTerm.find("Face");
|
||||
if (pos != std::string::npos) {
|
||||
return pruneLastTerm(longSubname);
|
||||
}
|
||||
|
||||
pos = lastTerm.find("Shell");
|
||||
if (pos != std::string::npos) {
|
||||
return pruneLastTerm(longSubname);
|
||||
}
|
||||
|
||||
pos = lastTerm.find("Solid");
|
||||
if (pos != std::string::npos) {
|
||||
return pruneLastTerm(longSubname);
|
||||
}
|
||||
|
||||
return longSubname;
|
||||
}
|
||||
|
||||
|
||||
//! remove the tnp information from a selection sub name returning a dot separated path
|
||||
//! Array001.Array001_i0.Array_i1.;Vertex33;:H1116,V.Vertex33 to
|
||||
//! Array001.Array001_i0.Array_i1.Vertex33
|
||||
std::string SubnameHelper::removeTnpInfo(const std::string& inString)
|
||||
{
|
||||
constexpr char TNPDelimiter {';'};
|
||||
size_t firstDelimiter = inString.find(TNPDelimiter);
|
||||
if (firstDelimiter == std::string::npos) {
|
||||
// no delimiter in string
|
||||
return inString;
|
||||
}
|
||||
auto geomName = getLastTerm(inString);
|
||||
auto path = inString.substr(0, firstDelimiter);
|
||||
auto result = path + geomName;
|
||||
return result;
|
||||
}
|
||||
57
src/Mod/Measure/App/SubnameHelper.h
Normal file
57
src/Mod/Measure/App/SubnameHelper.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2024 wandererfan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MEASURE_SUBNAMEMANIPULATOR_H
|
||||
#define MEASURE_SUBNAMEMANIPULATOR_H
|
||||
|
||||
#include <Mod/Measure/MeasureGlobal.h>
|
||||
|
||||
#include <TopoDS_Shape.hxx>
|
||||
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObserver.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Matrix.h>
|
||||
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
|
||||
namespace Measure
|
||||
{
|
||||
|
||||
//! a class to perform common operations on subelement names.
|
||||
class MeasureExport SubnameHelper
|
||||
{
|
||||
public:
|
||||
static std::string getLastTerm(const std::string& inString);
|
||||
static std::string getFirstTerm(const std::string& inString);
|
||||
static std::string namesToLongSub(const std::vector<std::string>& pathElementNames);
|
||||
static std::string pruneLastTerm(const std::string& inString);
|
||||
static std::string pruneFirstTerm(const std::string& inString);
|
||||
static std::string removeGeometryTerm(const std::string& longSubname);
|
||||
static std::string pathToLongSub(std::list<App::DocumentObject*> path);
|
||||
static std::string removeTnpInfo(const std::string& inString);
|
||||
};
|
||||
|
||||
} // namespace Measure
|
||||
|
||||
#endif // MEASURE_SUBNAMEMANIPULATOR_H
|
||||
@@ -44,7 +44,7 @@
|
||||
// // reference
|
||||
// replace(ref, newRef)
|
||||
// else:
|
||||
// // auto correct phase 2
|
||||
// // auto correct phase 2 - to be implemented
|
||||
// // 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
|
||||
@@ -68,6 +68,7 @@
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include <Mod/Measure/App/ShapeFinder.h>
|
||||
|
||||
#include "GeometryMatcher.h"
|
||||
#include "DimensionReferences.h"
|
||||
@@ -77,6 +78,7 @@
|
||||
#include "Preferences.h"
|
||||
|
||||
using namespace TechDraw;
|
||||
using namespace Measure;
|
||||
using DU = DrawUtil;
|
||||
|
||||
//! true if references point to valid geometry and the valid geometry matches the
|
||||
@@ -173,17 +175,17 @@ bool DimensionAutoCorrect::autocorrectReferences(std::vector<bool>& referenceSta
|
||||
continue;
|
||||
}
|
||||
|
||||
// we did not find an exact match, so check for an similar match
|
||||
// we did not find an exact match, so check for a similar match
|
||||
success = fix1GeomSimilar(fixedRef, savedGeometry.at(iRef).getShape());
|
||||
if (success) {
|
||||
// we did find an similar match
|
||||
// we did find a similar match
|
||||
referenceState.at(iRef) = true;
|
||||
repairedRefs.push_back(fixedRef);
|
||||
iRef++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// we did not find an similar match the geometry
|
||||
// we did not find a similar match the geometry
|
||||
result = false;
|
||||
referenceState.at(iRef) = false;
|
||||
repairedRefs.push_back(fixedRef);
|
||||
@@ -289,7 +291,8 @@ bool DimensionAutoCorrect::findExactEdge2d(ReferenceEntry& refToFix, const Part:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// no match
|
||||
|
||||
// no match, return the input reference
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -413,8 +416,16 @@ bool DimensionAutoCorrect::findSimilarEdge3d(ReferenceEntry& refToFix,
|
||||
bool DimensionAutoCorrect::isMatchingGeometry(const ReferenceEntry& ref,
|
||||
const Part::TopoShape& savedGeometry) const
|
||||
{
|
||||
// Base::Console().Message("DAC::isMatchingGeometry()\n");
|
||||
Part::TopoShape temp = ref.asCanonicalTopoShape();
|
||||
Part::TopoShape temp;
|
||||
if (ref.is3d()) {
|
||||
auto shape3d = ShapeFinder::getLocatedShape(*ref.getObject(), ref.getSubName(true));
|
||||
temp = Part::TopoShape(shape3d);
|
||||
} else {
|
||||
auto shape2d = ref.getGeometry();
|
||||
temp = Part::TopoShape(shape2d);
|
||||
}
|
||||
|
||||
|
||||
if (temp.isNull()) {
|
||||
// this shouldn't happen as we already know that this ref points to valid geometry
|
||||
return false;
|
||||
@@ -435,7 +446,7 @@ ReferenceEntry DimensionAutoCorrect::searchObjForVert(App::DocumentObject* obj,
|
||||
bool exact) const
|
||||
{
|
||||
(void)exact;
|
||||
auto shape3d = Part::Feature::getShape(obj);
|
||||
auto shape3d = ShapeFinder::getLocatedShape(*obj, "");
|
||||
if (shape3d.IsNull()) {
|
||||
// how to handle this?
|
||||
return {};
|
||||
@@ -443,7 +454,7 @@ ReferenceEntry DimensionAutoCorrect::searchObjForVert(App::DocumentObject* obj,
|
||||
auto vertsAll = getDimension()->getVertexes(shape3d);
|
||||
size_t iVert {1};
|
||||
for (auto& vert : vertsAll) {
|
||||
bool isSame = getMatcher()->compareGeometry(vert, refVertex);
|
||||
bool isSame = getMatcher()->compareGeometry(refVertex, vert);
|
||||
if (isSame) {
|
||||
auto newSubname = std::string("Vertex") + std::to_string(iVert);
|
||||
return {obj, newSubname, getDimension()->getDocument()};
|
||||
|
||||
@@ -33,7 +33,10 @@
|
||||
#include <App/GeoFeature.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/Link.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include <Mod/Measure/App/ShapeFinder.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include <Mod/PartDesign/App/Body.h>
|
||||
#include <Mod/PartDesign/App/Feature.h>
|
||||
@@ -45,11 +48,12 @@
|
||||
#include "CosmeticVertex.h"
|
||||
|
||||
using namespace TechDraw;
|
||||
using namespace Measure;
|
||||
using DU = DrawUtil;
|
||||
using SU = ShapeUtils;
|
||||
|
||||
|
||||
ReferenceEntry::ReferenceEntry( App::DocumentObject* docObject, std::string subName, App::Document* document)
|
||||
ReferenceEntry::ReferenceEntry( App::DocumentObject* docObject, const std::string& subName, App::Document* document)
|
||||
{
|
||||
setObject(docObject);
|
||||
setSubName(subName);
|
||||
@@ -66,7 +70,7 @@ ReferenceEntry::ReferenceEntry( App::DocumentObject* docObject, std::string subN
|
||||
ReferenceEntry::ReferenceEntry(const ReferenceEntry& other)
|
||||
{
|
||||
setObject(other.getObject());
|
||||
setSubName(other.getSubName());
|
||||
setSubName(other.getSubName(true));
|
||||
setObjectName(other.getObjectName());
|
||||
setDocument(other.getDocument());
|
||||
}
|
||||
@@ -79,7 +83,7 @@ ReferenceEntry& ReferenceEntry::operator=(const ReferenceEntry& otherRef)
|
||||
return *this;
|
||||
}
|
||||
setObject(otherRef.getObject());
|
||||
setSubName(otherRef.getSubName());
|
||||
setSubName(otherRef.getSubName(true));
|
||||
setObjectName(otherRef.getObjectName());
|
||||
setDocument(otherRef.getDocument());
|
||||
return *this;
|
||||
@@ -94,8 +98,6 @@ bool ReferenceEntry::operator==(const ReferenceEntry& otherRef) const
|
||||
|
||||
TopoDS_Shape ReferenceEntry::getGeometry() const
|
||||
{
|
||||
// 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) {
|
||||
@@ -112,24 +114,13 @@ TopoDS_Shape ReferenceEntry::getGeometry() const
|
||||
}
|
||||
|
||||
// 3d geometry
|
||||
Part::TopoShape shape = Part::Feature::getTopoShape(getObject());
|
||||
auto geoFeat = getObject<App::GeoFeature>();
|
||||
if (geoFeat) {
|
||||
shape.setPlacement(geoFeat->globalPlacement());
|
||||
}
|
||||
|
||||
if (getSubName().empty()) {
|
||||
return shape.getShape();
|
||||
}
|
||||
// TODO: what happens if the subelement is no longer present?
|
||||
return shape.getSubShape(getSubName().c_str());
|
||||
return ShapeFinder::getLocatedShape(*getObject(), getSubName(true));
|
||||
}
|
||||
|
||||
|
||||
//! get a shape for this 2d reference
|
||||
TopoDS_Shape ReferenceEntry::getGeometry2d() const
|
||||
{
|
||||
// Base::Console().Message("RE::getGeometry2d()\n");
|
||||
std::string gType;
|
||||
try {
|
||||
auto dvp = getObject<TechDraw::DrawViewPart>(); //NOLINT cppcoreguidelines-pro-type-static-cast-downcast
|
||||
@@ -160,7 +151,6 @@ TopoDS_Shape ReferenceEntry::getGeometry2d() const
|
||||
}
|
||||
catch (...) {
|
||||
Base::Console().Message("RE::getGeometry2d - no shape for dimension 2d reference - gType: **%s**\n", gType.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
@@ -172,12 +162,8 @@ std::string ReferenceEntry::getSubName(bool longForm) const
|
||||
if (longForm) {
|
||||
return m_subName;
|
||||
}
|
||||
std::string workingSubName(m_subName);
|
||||
size_t lastDot = workingSubName.rfind('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
workingSubName = workingSubName.substr(lastDot + 1);
|
||||
}
|
||||
return workingSubName;
|
||||
|
||||
return ShapeFinder::getLastTerm(m_subName);
|
||||
}
|
||||
|
||||
|
||||
@@ -198,10 +184,8 @@ App::DocumentObject* ReferenceEntry::getObject() const
|
||||
//! return the reference geometry as a Part::TopoShape.
|
||||
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");
|
||||
return {};
|
||||
}
|
||||
if (geom.ShapeType() == TopAbs_VERTEX) {
|
||||
@@ -222,7 +206,6 @@ Part::TopoShape ReferenceEntry::asTopoShape() const
|
||||
//! returns unscaled, unrotated version of inShape. inShape is assumed to be a 2d shape, but this is not enforced.
|
||||
Part::TopoShape ReferenceEntry::asCanonicalTopoShape() const
|
||||
{
|
||||
// Base::Console().Message("RE::asCanonicalTopoShape()\n");
|
||||
if (is3d()) {
|
||||
return asTopoShape();
|
||||
}
|
||||
@@ -240,7 +223,6 @@ Part::TopoShape ReferenceEntry::asCanonicalTopoShape() const
|
||||
//! operations.
|
||||
Part::TopoShape ReferenceEntry::asCanonicalTopoShape(const Part::TopoShape& inShape, const DrawViewPart& dvp)
|
||||
{
|
||||
// Base::Console().Message("RE::(static)asCanonicalTopoShape()\n");
|
||||
gp_Ax2 OXYZ;
|
||||
auto unscaledShape = SU::scaleShape(inShape.getShape(), 1.0 / dvp.getScale());
|
||||
if (dvp.Rotation.getValue() != 0.0) {
|
||||
@@ -270,7 +252,6 @@ Part::TopoShape ReferenceEntry::asTopoShapeFace(const TopoDS_Face &face)
|
||||
|
||||
std::string ReferenceEntry::geomType() const
|
||||
{
|
||||
// Base::Console().Message("RE::geomType() - subName: **%s**\n", getSubName().c_str());
|
||||
return DrawUtil::getGeomTypeFromName(getSubName());
|
||||
}
|
||||
|
||||
@@ -295,14 +276,21 @@ bool ReferenceEntry::isWholeObject() const
|
||||
//! true if this reference point to 3d model geometry
|
||||
bool ReferenceEntry::is3d() const
|
||||
{
|
||||
if (!getObject()) {
|
||||
// we should really fail here?
|
||||
return false;
|
||||
}
|
||||
if (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;
|
||||
}
|
||||
|
||||
@@ -310,7 +298,6 @@ bool ReferenceEntry::is3d() const
|
||||
//! true if the target of this reference has a shape
|
||||
bool ReferenceEntry::hasGeometry() const
|
||||
{
|
||||
// Base::Console().Message("RE::hasGeometry()\n");
|
||||
if (!getObject()) {
|
||||
return false;
|
||||
}
|
||||
@@ -321,6 +308,7 @@ bool ReferenceEntry::hasGeometry() const
|
||||
}
|
||||
|
||||
// 3d reference
|
||||
// TODO: shouldn't this be ShapeFinder.getLocatedShape?
|
||||
auto shape = Part::Feature::getTopoShape(getObject());
|
||||
auto subShape = shape.getSubShape(getSubName().c_str());
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class TechDrawExport ReferenceEntry
|
||||
{
|
||||
public:
|
||||
ReferenceEntry() = default;
|
||||
ReferenceEntry( App::DocumentObject* docObject, std::string subName, App::Document* document = nullptr);
|
||||
ReferenceEntry( App::DocumentObject* docObject, const std::string& subName, App::Document* document = nullptr);
|
||||
ReferenceEntry(const ReferenceEntry& other);
|
||||
~ReferenceEntry() = default;
|
||||
|
||||
|
||||
@@ -645,7 +645,6 @@ QStringList DrawViewDimension::getPrefixSuffixSpec(const QString &fSpec)
|
||||
//! NOTE: this returns the Dimension value in internal units (ie mm)!!!!
|
||||
double DrawViewDimension::getDimValue()
|
||||
{
|
||||
// Base::Console().Message("DVD::getDimValue()\n");
|
||||
constexpr double CircleDegrees{360.0};
|
||||
double result = 0.0;
|
||||
if (!has2DReferences() && !has3DReferences()) {
|
||||
@@ -703,7 +702,7 @@ double DrawViewDimension::getTrueDimValue() const
|
||||
result = measurement->radius();
|
||||
}
|
||||
else if (Type.isValue("Diameter")) {
|
||||
result = 2.0 * measurement->radius();
|
||||
result = 2 * measurement->radius();
|
||||
}
|
||||
else if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) {
|
||||
result = measurement->angle();
|
||||
@@ -720,7 +719,6 @@ double DrawViewDimension::getTrueDimValue() const
|
||||
//! retrieve the dimension value for "projected" (2d) dimensions. The returned value is in internal units (mm).
|
||||
double DrawViewDimension::getProjectedDimValue() const
|
||||
{
|
||||
// Base::Console().Message("DVD::getProjectedDimValue()\n");
|
||||
double result = 0.0;
|
||||
double scale = getViewPart()->getScale();
|
||||
|
||||
@@ -736,7 +734,8 @@ double DrawViewDimension::getProjectedDimValue() const
|
||||
// then we should not move the points.
|
||||
//
|
||||
pts.invertY();
|
||||
pts.scale(1.0 / scale);
|
||||
// unscale the points, map them to the broken view then rescale them to draw.
|
||||
pts.scale(1 / scale);
|
||||
pts.first(dbv->mapPoint2dFromView(pts.first()));
|
||||
pts.second(dbv->mapPoint2dFromView(pts.second()));
|
||||
pts.invertY();
|
||||
@@ -808,7 +807,6 @@ pointPair DrawViewDimension::getLinearPoints() const
|
||||
|
||||
pointPair DrawViewDimension::getPointsOneEdge(ReferenceVector references)
|
||||
{
|
||||
// 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())
|
||||
@@ -849,7 +847,6 @@ pointPair DrawViewDimension::getPointsOneEdge(ReferenceVector references)
|
||||
|
||||
pointPair DrawViewDimension::getPointsTwoEdges(ReferenceVector references)
|
||||
{
|
||||
// Base::Console().Message("DVD::getPointsTwoEdges() - %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());
|
||||
@@ -882,7 +879,6 @@ pointPair DrawViewDimension::getPointsTwoEdges(ReferenceVector references)
|
||||
|
||||
pointPair DrawViewDimension::getPointsTwoVerts(ReferenceVector references)
|
||||
{
|
||||
// Base::Console().Message("DVD::getPointsTwoVerts() - %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());
|
||||
@@ -920,7 +916,6 @@ pointPair DrawViewDimension::getPointsTwoVerts(ReferenceVector references)
|
||||
|
||||
pointPair DrawViewDimension::getPointsEdgeVert(ReferenceVector references)
|
||||
{
|
||||
// Base::Console().Message("DVD::getPointsEdgeVert() - %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());
|
||||
@@ -978,7 +973,6 @@ pointPair DrawViewDimension::getPointsEdgeVert(ReferenceVector references)
|
||||
|
||||
arcPoints DrawViewDimension::getArcParameters(ReferenceVector references)
|
||||
{
|
||||
// Base::Console().Message("DVD::getArcParameters()\n");
|
||||
App::DocumentObject* refObject = references.front().getObject();
|
||||
int iSubelement = DrawUtil::getIndexFromName(references.front().getSubName());
|
||||
if (refObject->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())
|
||||
@@ -1039,7 +1033,7 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base)
|
||||
if (ellipse->closed()) {
|
||||
double r1 = ellipse->minor;
|
||||
double r2 = ellipse->major;
|
||||
double rAvg = (r1 + r2) / 2.0;
|
||||
double rAvg = (r1 + r2) / 2;
|
||||
pts.center = Base::Vector3d(ellipse->center.x, ellipse->center.y, 0.0);
|
||||
pts.radius = rAvg;
|
||||
pts.isArc = false;
|
||||
@@ -1052,7 +1046,7 @@ arcPoints DrawViewDimension::arcPointsFromBaseGeom(TechDraw::BaseGeomPtr base)
|
||||
TechDraw::AOEPtr aoe = std::static_pointer_cast<TechDraw::AOE>(base);
|
||||
double r1 = aoe->minor;
|
||||
double r2 = aoe->major;
|
||||
double rAvg = (r1 + r2) / 2.0;
|
||||
double rAvg = (r1 + r2) / 2;
|
||||
pts.isArc = true;
|
||||
pts.center = Base::Vector3d(aoe->center.x, aoe->center.y, 0.0);
|
||||
pts.radius = rAvg;
|
||||
@@ -1113,10 +1107,12 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge)
|
||||
arcPoints pts;
|
||||
pts.isArc = !BRep_Tool::IsClosed(occEdge);
|
||||
pts.arcCW = false;
|
||||
|
||||
// get all the common information for circle, ellipse and bspline conversions
|
||||
BRepAdaptor_Curve adapt(occEdge);
|
||||
double pFirst = adapt.FirstParameter();
|
||||
double pLast = adapt.LastParameter();
|
||||
double pMid = (pFirst + pLast) / 2.0;
|
||||
double pMid = (pFirst + pLast) / 2;
|
||||
BRepLProp_CLProps props(adapt, pFirst, 0, Precision::Confusion());
|
||||
pts.arcEnds.first(DrawUtil::toVector3d(props.Value()));
|
||||
props.SetParameter(pLast);
|
||||
@@ -1208,7 +1204,6 @@ arcPoints DrawViewDimension::arcPointsFromEdge(TopoDS_Edge occEdge)
|
||||
|
||||
anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references)
|
||||
{
|
||||
// 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());
|
||||
@@ -1343,7 +1338,6 @@ anglePoints DrawViewDimension::getAnglePointsTwoEdges(ReferenceVector references
|
||||
// somewhere?
|
||||
anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector references)
|
||||
{
|
||||
// Base::Console().Message("DVD::getAnglePointsThreeVerts() - %s\n", getNameInDocument());
|
||||
if (references.size() < 3) {
|
||||
throw Base::RuntimeError("Not enough references to make angle dimension");
|
||||
}
|
||||
@@ -1478,7 +1472,6 @@ DrawViewPart* DrawViewDimension::getViewPart() const
|
||||
// subName)
|
||||
ReferenceVector DrawViewDimension::getEffectiveReferences() const
|
||||
{
|
||||
// Base::Console().Message("DVD::getEffectiveReferences()\n");
|
||||
const std::vector<App::DocumentObject*>& objects3d = References3D.getValues();
|
||||
const std::vector<std::string>& subElements3d = References3D.getSubValues();
|
||||
const std::vector<App::DocumentObject*>& objects = References2D.getValues();
|
||||
@@ -1549,7 +1542,7 @@ int DrawViewDimension::getRefType() const
|
||||
// decide what the reference configuration is by examining the names of the sub elements
|
||||
int DrawViewDimension::getRefTypeSubElements(const std::vector<std::string>& subElements)
|
||||
{
|
||||
int refType = invalidRef;
|
||||
int refType{invalidRef};
|
||||
int refEdges{0};
|
||||
int refVertices{0};
|
||||
int refFaces{0};
|
||||
@@ -1591,7 +1584,6 @@ int DrawViewDimension::getRefTypeSubElements(const std::vector<std::string>& sub
|
||||
//! validate 2D references - only checks if the target exists
|
||||
bool DrawViewDimension::checkReferences2D() const
|
||||
{
|
||||
// Base::Console().Message("DVD::checkReferences2d() - %s\n", getNameInDocument());
|
||||
const std::vector<App::DocumentObject*>& objects = References2D.getValues();
|
||||
if (objects.empty()) {
|
||||
return false;
|
||||
@@ -1650,8 +1642,6 @@ bool DrawViewDimension::hasBroken3dReferences() const
|
||||
|
||||
void DrawViewDimension::updateSavedGeometry()
|
||||
{
|
||||
// Base::Console().Message("DVD::updateSavedGeometry() - %s - savedGeometry: %d\n",
|
||||
// getNameInDocument(), SavedGeometry.getValues().size());
|
||||
ReferenceVector references = getEffectiveReferences();
|
||||
if (references.empty()) {
|
||||
// no references to save
|
||||
@@ -1719,6 +1709,20 @@ std::vector<TopoShape> DrawViewDimension::getVertexes(const TopoShape& inShape)
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! returns the angle subtended by an arc from 3 points.
|
||||
double DrawViewDimension::getArcAngle(Base::Vector3d center, Base::Vector3d startPoint, Base::Vector3d endPoint)
|
||||
{
|
||||
auto leg0 = startPoint - center;
|
||||
auto leg1 = endPoint - startPoint;
|
||||
auto referenceDirection = leg0.Cross(leg1);
|
||||
gp_Ax1 axis{DU::togp_Pnt(center), DU::togp_Vec(referenceDirection)};
|
||||
gp_Vec startVec = DrawUtil::togp_Vec(leg0);
|
||||
gp_Vec endVec = DrawUtil::togp_Vec(leg1);
|
||||
double angle = startVec.AngleWithRef(endVec, axis.Direction().XYZ());
|
||||
return angle;
|
||||
}
|
||||
|
||||
|
||||
pointPair DrawViewDimension::closestPoints(TopoDS_Shape s1, TopoDS_Shape s2) const
|
||||
{
|
||||
pointPair result;
|
||||
@@ -1740,7 +1744,6 @@ pointPair DrawViewDimension::closestPoints(TopoDS_Shape s1, TopoDS_Shape s2) con
|
||||
// set the reference property from a reference vector
|
||||
void DrawViewDimension::setReferences2d(const ReferenceVector& refsAll)
|
||||
{
|
||||
// Base::Console().Message("DVD::setReferences2d(%d)\n", refs.size());
|
||||
std::vector<App::DocumentObject*> objects;
|
||||
std::vector<std::string> subNames;
|
||||
if (objects.size() != subNames.size()) {
|
||||
@@ -1759,7 +1762,6 @@ void DrawViewDimension::setReferences2d(const ReferenceVector& refsAll)
|
||||
// set the reference property from a reference vector
|
||||
void DrawViewDimension::setReferences3d(const ReferenceVector &refsAll)
|
||||
{
|
||||
// Base::Console().Message("DVD::setReferences3d()\n");
|
||||
if (refsAll.empty() && !References3D.getValues().empty()) {
|
||||
// clear the property of any old links
|
||||
References3D.setValue(nullptr, nullptr);
|
||||
@@ -1773,7 +1775,7 @@ void DrawViewDimension::setReferences3d(const ReferenceVector &refsAll)
|
||||
|
||||
for (auto& ref : refsAll) {
|
||||
objects.push_back(ref.getObject());
|
||||
subNames.push_back(ref.getSubName());
|
||||
subNames.push_back(ref.getSubName(true));
|
||||
// cache the referenced object
|
||||
m_3dObjectCache.insert(ref.getObject()->getNameInDocument());
|
||||
// cache the parent object if available. Ideally, we would handle deletion
|
||||
@@ -1794,7 +1796,6 @@ void DrawViewDimension::setReferences3d(const ReferenceVector &refsAll)
|
||||
//! add Dimension 3D references to measurement
|
||||
void DrawViewDimension::setAll3DMeasurement()
|
||||
{
|
||||
// Base::Console().Message("DVD::setAll3dMeasurement()\n");
|
||||
measurement->clear();
|
||||
const std::vector<App::DocumentObject*>& Objs = References3D.getValues();
|
||||
const std::vector<std::string>& Subs = References3D.getSubValues();
|
||||
@@ -1820,7 +1821,6 @@ void DrawViewDimension::setAll3DMeasurement()
|
||||
//! dimension.
|
||||
bool DrawViewDimension::validateReferenceForm() const
|
||||
{
|
||||
// Base::Console().Message("DVD::validateReferenceForm()\n");
|
||||
// we have either or both valid References3D and References2D
|
||||
ReferenceVector references = getEffectiveReferences();
|
||||
if (references.empty()) {
|
||||
@@ -1925,8 +1925,8 @@ void DrawViewDimension::dumpRefs2D(const char* text) const
|
||||
Base::Console().Message("DUMP - %s\n", text);
|
||||
const std::vector<App::DocumentObject*>& objects = References2D.getValues();
|
||||
const std::vector<std::string>& subElements = References2D.getSubValues();
|
||||
std::vector<App::DocumentObject*>::const_iterator objIt = objects.begin();
|
||||
std::vector<std::string>::const_iterator subIt = subElements.begin();
|
||||
auto objIt = objects.begin();
|
||||
auto subIt = subElements.begin();
|
||||
int i = 0;
|
||||
for (; objIt != objects.end(); objIt++, subIt++, i++) {
|
||||
Base::Console().Message("DUMP - ref: %d object: %s subElement: %s\n",
|
||||
@@ -1936,6 +1936,7 @@ void DrawViewDimension::dumpRefs2D(const char* text) const
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this should go into DrawUtil or ShapeUtil or ??
|
||||
double DrawViewDimension::dist2Segs(Base::Vector3d s1,
|
||||
Base::Vector3d e1,
|
||||
Base::Vector3d s2,
|
||||
@@ -2043,7 +2044,6 @@ pointPair DrawViewDimension::getArrowPositions()
|
||||
|
||||
bool DrawViewDimension::has2DReferences() const
|
||||
{
|
||||
// Base::Console().Message("DVD::has2DReferences() - %s\n",getNameInDocument());
|
||||
const std::vector<App::DocumentObject*>& objects = References2D.getValues();
|
||||
const std::vector<std::string>& subNames = References2D.getSubValues();
|
||||
if (objects.empty()) {
|
||||
@@ -2120,6 +2120,8 @@ PyObject* DrawViewDimension::getPyObject()
|
||||
return Py::new_reference_to(PythonObject);
|
||||
}
|
||||
|
||||
|
||||
//! store the corners of this dimension's base view for use by phase 2 of the auto correct process.
|
||||
void DrawViewDimension::saveFeatureBox()
|
||||
{
|
||||
std::vector<Base::Vector3d> bbxCorners;
|
||||
|
||||
@@ -204,9 +204,9 @@ public:
|
||||
return m_corrector;
|
||||
}
|
||||
|
||||
// these should probably be static as they don't use the dimension at all
|
||||
std::vector<Part::TopoShape> getEdges(const Part::TopoShape& inShape);
|
||||
std::vector<Part::TopoShape> getVertexes(const Part::TopoShape& inShape);
|
||||
static std::vector<Part::TopoShape> getEdges(const Part::TopoShape& inShape);
|
||||
static std::vector<Part::TopoShape> getVertexes(const Part::TopoShape& inShape);
|
||||
static double getArcAngle(Base::Vector3d center, Base::Vector3d startPoint, Base::Vector3d endPoint);
|
||||
|
||||
// autocorrect support methods
|
||||
void saveFeatureBox();
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <Mod/Part/App/PrimitiveFeature.h>
|
||||
#include <Mod/Part/App/FeaturePartCircle.h>
|
||||
#include <Mod/Part/App/TopoShapePy.h>
|
||||
#include <Mod/Measure/App/ShapeFinder.h>
|
||||
//#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "ShapeExtractor.h"
|
||||
@@ -55,6 +56,7 @@
|
||||
#include "Preferences.h"
|
||||
|
||||
using namespace TechDraw;
|
||||
using namespace Measure;
|
||||
using DU = DrawUtil;
|
||||
using SU = ShapeUtils;
|
||||
|
||||
@@ -166,7 +168,7 @@ TopoDS_Shape ShapeExtractor::getShapes(const std::vector<App::DocumentObject*> l
|
||||
continue;
|
||||
} else if (s.ShapeType() < TopAbs_SOLID) {
|
||||
//clean up composite shapes
|
||||
TopoDS_Shape cleanShape = stripInfiniteShapes(s);
|
||||
TopoDS_Shape cleanShape = ShapeFinder::ShapeFinder::stripInfiniteShapes(s);
|
||||
if (!cleanShape.IsNull()) {
|
||||
builder.Add(comp, cleanShape);
|
||||
}
|
||||
@@ -226,7 +228,7 @@ std::vector<TopoDS_Shape> ShapeExtractor::getXShapes(const App::Link* xLink)
|
||||
auto shape = Part::Feature::getShape(l); // TODO: getTopoShape() ?
|
||||
Part::TopoShape ts(shape);
|
||||
if (ts.isInfinite()) {
|
||||
shape = stripInfiniteShapes(shape);
|
||||
shape = ShapeFinder::stripInfiniteShapes(shape);
|
||||
}
|
||||
if (!checkShape(l, shape)) {
|
||||
continue;
|
||||
@@ -285,7 +287,7 @@ TopoDS_Shape ShapeExtractor::getShapeFromXLink(const App::Link* xLink)
|
||||
}
|
||||
Part::TopoShape ts(shape);
|
||||
if (ts.isInfinite()) {
|
||||
shape = stripInfiniteShapes(shape);
|
||||
shape = ShapeFinder::stripInfiniteShapes(shape);
|
||||
ts = Part::TopoShape(shape);
|
||||
}
|
||||
//ts might be garbage now, better check
|
||||
@@ -379,30 +381,6 @@ TopoDS_Shape ShapeExtractor::getShapesFused(const std::vector<App::DocumentObjec
|
||||
return baseShape;
|
||||
}
|
||||
|
||||
//inShape is a compound
|
||||
//The shapes of datum features (Axis, Plan and CS) are infinite
|
||||
//Infinite shapes can not be projected, so they need to be removed.
|
||||
TopoDS_Shape ShapeExtractor::stripInfiniteShapes(TopoDS_Shape inShape)
|
||||
{
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound comp;
|
||||
builder.MakeCompound(comp);
|
||||
|
||||
TopoDS_Iterator it(inShape);
|
||||
for (; it.More(); it.Next()) {
|
||||
TopoDS_Shape s = it.Value();
|
||||
if (s.ShapeType() < TopAbs_SOLID) {
|
||||
//look inside composite shapes
|
||||
s = stripInfiniteShapes(s);
|
||||
} else if (Part::TopoShape(s).isInfinite()) {
|
||||
continue;
|
||||
} else {
|
||||
//simple shape
|
||||
}
|
||||
builder.Add(comp, s);
|
||||
}
|
||||
return TopoDS_Shape(std::move(comp));
|
||||
}
|
||||
|
||||
bool ShapeExtractor::is2dObject(const App::DocumentObject* obj)
|
||||
{
|
||||
|
||||
@@ -42,9 +42,10 @@ public:
|
||||
static TopoDS_Shape getShapes(const std::vector<App::DocumentObject*> links, bool include2d = true);
|
||||
static std::vector<TopoDS_Shape> getShapes2d(const std::vector<App::DocumentObject*> links);
|
||||
static std::vector<TopoDS_Shape> getXShapes(const App::Link* xLink);
|
||||
static std::vector<TopoDS_Shape> getShapesFromObject(const App::DocumentObject* docObj);
|
||||
static TopoDS_Shape getShapesFused(const std::vector<App::DocumentObject*> links);
|
||||
static TopoDS_Shape getShapeFromXLink(const App::Link* xLink);
|
||||
static std::vector<TopoDS_Shape> getShapesFromXRoot(const App::DocumentObject *xLinkRoot);
|
||||
static std::vector<TopoDS_Shape> getShapesFromObject(const App::DocumentObject* docObj);
|
||||
|
||||
static bool is2dObject(const App::DocumentObject* obj);
|
||||
static bool isEdgeType(const App::DocumentObject* obj);
|
||||
@@ -52,14 +53,19 @@ public:
|
||||
static bool isDraftPoint(const App::DocumentObject* obj);
|
||||
static bool isDatumPoint(const App::DocumentObject* obj);
|
||||
static bool isSketchObject(const App::DocumentObject* obj);
|
||||
static bool isExplodedAssembly(const App::DocumentObject* obj);
|
||||
|
||||
static Base::Vector3d getLocation3dFromFeat(const App::DocumentObject *obj);
|
||||
|
||||
static TopoDS_Shape stripInfiniteShapes(TopoDS_Shape inShape);
|
||||
|
||||
static TopoDS_Shape getLocatedShape(const App::DocumentObject* docObj);
|
||||
|
||||
static bool checkShape(const App::DocumentObject* shapeObj, TopoDS_Shape shape);
|
||||
|
||||
static App::DocumentObject* getExplodedAssembly(std::vector<TopoDS_Shape>& sourceShapes,
|
||||
App::DocumentObject* link);
|
||||
static void restoreExplodedAssembly(App::DocumentObject* link);
|
||||
|
||||
static App::DocumentObject* getLinkedObject(const App::DocumentObject* root);
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Gui/Selection.h>
|
||||
#include <Mod/Measure/App/ShapeFinder.h>
|
||||
#include <Mod/TechDraw/App/DrawViewPart.h>
|
||||
#include <Mod/TechDraw/App/ShapeExtractor.h>
|
||||
|
||||
@@ -37,6 +38,7 @@
|
||||
|
||||
|
||||
using namespace TechDraw;
|
||||
using namespace Measure;
|
||||
using DU = DrawUtil;
|
||||
|
||||
TechDraw::DrawViewPart* TechDraw::getReferencesFromSelection(ReferenceVector& references2d,
|
||||
@@ -44,15 +46,21 @@ TechDraw::DrawViewPart* TechDraw::getReferencesFromSelection(ReferenceVector& re
|
||||
{
|
||||
TechDraw::DrawViewPart* dvp(nullptr);
|
||||
TechDraw::DrawViewDimension* dim(nullptr);
|
||||
std::vector<Gui::SelectionObject> selectionAll = Gui::Selection().getSelectionEx();
|
||||
constexpr bool allowOnlySingle{false};
|
||||
std::vector<Gui::SelectionObject> selectionAll =
|
||||
Gui::Selection().getSelectionEx("*",
|
||||
App::DocumentObject::getClassTypeId(),
|
||||
Gui::ResolveMode::NoResolve,
|
||||
allowOnlySingle);
|
||||
|
||||
for (auto& selItem : selectionAll) {
|
||||
if (selItem.getObject()->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())) {
|
||||
//we are probably repairing a dimension, but we will check later
|
||||
dim = static_cast<TechDraw::DrawViewDimension*>(selItem.getObject());
|
||||
dim = static_cast<TechDraw::DrawViewDimension*>(selItem.getObject()); //NOLINT cppcoreguidelines-pro-type-static-cast-downcast
|
||||
} else if (selItem.getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) {
|
||||
//this could be a 2d geometry selection or just a DrawViewPart for context in
|
||||
//a 3d selection
|
||||
dvp = static_cast<TechDraw::DrawViewPart*>(selItem.getObject());
|
||||
dvp = static_cast<TechDraw::DrawViewPart*>(selItem.getObject()); //NOLINT cppcoreguidelines-pro-type-static-cast-downcast
|
||||
if (selItem.getSubNames().empty()) {
|
||||
//there are no subNames, so we think this is a 3d case,
|
||||
//and we only need to select the view. We set the reference
|
||||
@@ -62,38 +70,17 @@ TechDraw::DrawViewPart* TechDraw::getReferencesFromSelection(ReferenceVector& re
|
||||
continue;
|
||||
}
|
||||
for (auto& sub : selItem.getSubNames()) {
|
||||
ReferenceEntry ref(dvp, sub);
|
||||
// plain ordinary 2d view + geometry reference
|
||||
|
||||
ReferenceEntry ref(dvp, ShapeFinder::getLastTerm(sub));
|
||||
references2d.push_back(ref);
|
||||
}
|
||||
} else if (!selItem.getObject()->isDerivedFrom(TechDraw::DrawView::getClassTypeId())) {
|
||||
//this is not a TechDraw object, so we check to see if it has 3d geometry
|
||||
std::vector<App::DocumentObject*> links;
|
||||
links.push_back(selItem.getObject());
|
||||
if (!ShapeExtractor::getShapes(links).IsNull()) {
|
||||
//this item has 3d geometry so we are interested
|
||||
App::DocumentObject* obj3d = selItem.getObject();
|
||||
if (selItem.getSubNames().empty()) {
|
||||
if (ShapeExtractor::isPointType(obj3d)) {
|
||||
//a point object may not have a subName when selected,
|
||||
//so we need to perform some special handling.
|
||||
ReferenceEntry ref(obj3d, "Vertex1");
|
||||
references3d.push_back(ref);
|
||||
continue;
|
||||
} else {
|
||||
//this is a whole object reference, probably for an extent dimension
|
||||
ReferenceEntry ref(obj3d, std::string());
|
||||
references3d.push_back(ref);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//this is a regular reference in form obj+subelement
|
||||
for (auto& sub3d : selItem.getSubNames()) {
|
||||
ReferenceEntry ref(obj3d, sub3d);
|
||||
references3d.push_back(ref);
|
||||
}
|
||||
} else {
|
||||
Base::Console().Message("DV::getRefsFromSel - %s has no shape!\n",
|
||||
selItem.getObject()->getNameInDocument());
|
||||
App::DocumentObject* obj3d = selItem.getObject();
|
||||
// this is a regular 3d reference in form obj + long subelement
|
||||
for (auto& sub3d : selItem.getSubNames()) {
|
||||
ReferenceEntry ref(obj3d, sub3d);
|
||||
references3d.push_back(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,15 +96,15 @@ TechDraw::DrawViewPart* TechDraw::getReferencesFromSelection(ReferenceVector& re
|
||||
|
||||
//! verify that the proposed references contains valid geometries from a 2d DrawViewPart.
|
||||
DimensionGeometryType TechDraw::validateDimSelection(
|
||||
ReferenceVector references, //[(dvp*, std::string),...,(dvp*, std::string)]
|
||||
StringVector acceptableGeometry,//"Edge", "Vertex", etc
|
||||
std::vector<int> minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
std::vector<DimensionGeometryType> acceptableDimensionGeometrys)//isVertical, isHorizontal, ...
|
||||
const ReferenceVector& references, //[(dvp*, std::string),...,(dvp*, std::string)]
|
||||
const StringVector& acceptableGeometry,//"Edge", "Vertex", etc
|
||||
const std::vector<int>& minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
const std::vector<DimensionGeometryType>& acceptableDimensionGeometrys)//isVertical, isHorizontal, ...
|
||||
{
|
||||
StringVector subNames;
|
||||
TechDraw::DrawViewPart* dvpSave(nullptr);
|
||||
for (auto& ref : references) {
|
||||
auto* dvp = dynamic_cast<TechDraw::DrawViewPart*>(ref.getObject());
|
||||
auto dvp = dynamic_cast<TechDraw::DrawViewPart*>(ref.getObject());
|
||||
if (dvp) {
|
||||
dvpSave = dvp;
|
||||
if (!ref.getSubName().empty()) {
|
||||
@@ -181,15 +168,15 @@ DimensionGeometryType TechDraw::validateDimSelection(
|
||||
//! verify that the proposed references contains valid geometries from non-TechDraw objects.
|
||||
DimensionGeometryType TechDraw::validateDimSelection3d(
|
||||
TechDraw::DrawViewPart* dvp,
|
||||
ReferenceVector references, //[(dvp*, std::string),...,(dvp*, std::string)]
|
||||
StringVector acceptableGeometry,//"Edge", "Vertex", etc
|
||||
std::vector<int> minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
std::vector<DimensionGeometryType> acceptableDimensionGeometrys)//isVertical, isHorizontal, ...
|
||||
const ReferenceVector& references, //[(dvp*, std::string),...,(dvp*, std::string)]
|
||||
const StringVector& acceptableGeometry,//"Edge", "Vertex", etc
|
||||
const std::vector<int>& minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
const std::vector<DimensionGeometryType>& acceptableDimensionGeometrys)//isVertical, isHorizontal, ...
|
||||
{
|
||||
StringVector subNames;
|
||||
for (auto& ref : references) {
|
||||
if (!ref.getSubName().empty()) {
|
||||
subNames.push_back(ref.getSubName());
|
||||
subNames.push_back(ref.getSubName(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +212,7 @@ DimensionGeometryType TechDraw::validateDimSelection3d(
|
||||
bool TechDraw::validateSubnameList(StringVector subNames, GeometrySet acceptableGeometrySet)
|
||||
{
|
||||
for (auto& sub : subNames) {
|
||||
std::string geometryType = DrawUtil::getGeomTypeFromName(sub);
|
||||
std::string geometryType = DrawUtil::getGeomTypeFromName(ShapeFinder::getLastTerm(sub));
|
||||
if (acceptableGeometrySet.count(geometryType) == 0) {
|
||||
//this geometry type is not allowed
|
||||
return false;
|
||||
@@ -240,7 +227,7 @@ bool TechDraw::checkGeometryOccurrences(StringVector subNames, GeomCountMap keye
|
||||
//how many of each geometry descriptor are input
|
||||
GeomCountMap foundCounts;
|
||||
for (auto& sub : subNames) {
|
||||
std::string geometryType = DrawUtil::getGeomTypeFromName(sub);
|
||||
std::string geometryType = DrawUtil::getGeomTypeFromName(ShapeFinder::getLastTerm(sub));
|
||||
std::map<std::string, int>::iterator it0(foundCounts.find(geometryType));
|
||||
if (it0 == foundCounts.end()) {
|
||||
//first occurrence of this geometryType
|
||||
@@ -355,8 +342,8 @@ DimensionGeometryType TechDraw::getGeometryConfiguration3d(DrawViewPart* dvp,
|
||||
|
||||
//fill the GeomCountMap with pairs made from corresponding items in acceptableGeometry
|
||||
//and minimumCounts
|
||||
GeomCountMap TechDraw::loadRequiredCounts(StringVector& acceptableGeometry,
|
||||
std::vector<int>& minimumCounts)
|
||||
GeomCountMap TechDraw::loadRequiredCounts(const StringVector& acceptableGeometry,
|
||||
const std::vector<int>& minimumCounts)
|
||||
{
|
||||
if (acceptableGeometry.size() != minimumCounts.size()) {
|
||||
throw Base::IndexError("acceptableGeometry and minimum counts have different sizes.");
|
||||
|
||||
@@ -66,16 +66,15 @@ enum DimensionGeometryEnum {
|
||||
|
||||
DrawViewPart* getReferencesFromSelection(ReferenceVector& references2d,
|
||||
ReferenceVector& references3d);
|
||||
DimensionGeometryType validateDimSelection(
|
||||
ReferenceVector references,
|
||||
StringVector acceptableGeometry,//"Edge", "Vertex", etc
|
||||
std::vector<int> minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
std::vector<DimensionGeometryType> acceptableDimensionGeometrys);//isVertical, isHorizontal, ...
|
||||
DimensionGeometryType validateDimSelection3d(
|
||||
DrawViewPart* dvp, ReferenceVector references,
|
||||
StringVector acceptableGeometry,//"Edge", "Vertex", etc
|
||||
std::vector<int> minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
std::vector<DimensionGeometryType> acceptableDimensionGeometrys);//isVertical, isHorizontal, ...
|
||||
DimensionGeometryType validateDimSelection(const ReferenceVector& references,
|
||||
const StringVector& acceptableGeometry,//"Edge", "Vertex", etc
|
||||
const std::vector<int>& minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
const std::vector<DimensionGeometryType>& acceptableDimensionGeometrys);//isVertical, isHorizontal, ...
|
||||
DimensionGeometryType validateDimSelection3d(DrawViewPart* dvp,
|
||||
const ReferenceVector& references,
|
||||
const StringVector& acceptableGeometry, //"Edge", "Vertex", etc
|
||||
const std::vector<int>& minimumCounts, //how many of each geometry are needed for a good dimension
|
||||
const std::vector<DimensionGeometryType>& acceptableDimensionGeometrys);//isVertical, isHorizontal, ...
|
||||
|
||||
bool validateSubnameList(StringVector subNames, GeometrySet acceptableGeometrySet);
|
||||
|
||||
@@ -83,8 +82,8 @@ DimensionGeometryType getGeometryConfiguration(ReferenceVector valid2dReferences
|
||||
DimensionGeometryType getGeometryConfiguration3d(DrawViewPart* dvp,
|
||||
ReferenceVector valid3dReferences);
|
||||
|
||||
GeomCountMap loadRequiredCounts(StringVector& acceptableGeometry,
|
||||
std::vector<int>& minimumCouts);
|
||||
GeomCountMap loadRequiredCounts(const StringVector& acceptableGeometry,
|
||||
const std::vector<int>& minimumCouts);
|
||||
bool checkGeometryOccurrences(StringVector subNames, GeomCountMap keyedMinimumCounts);
|
||||
|
||||
DimensionGeometryType isValidVertexes(ReferenceVector refs);
|
||||
|
||||
Reference in New Issue
Block a user