From f4c90c07f1c0061fe8ca067f51c39c4e5e56592f Mon Sep 17 00:00:00 2001 From: hlorus Date: Sun, 14 Jan 2024 21:31:26 +0100 Subject: [PATCH] [Part] Add handlers for unified measurement facility --- src/Mod/Part/App/AppPart.cpp | 6 +- src/Mod/Part/App/CMakeLists.txt | 3 + src/Mod/Part/App/Measure.cpp | 440 +++++++++++++++++++++++++++++++ src/Mod/Part/App/MeasureClient.h | 52 ++++ src/Mod/Part/App/MeasureInfo.h | 160 +++++++++++ 5 files changed, 660 insertions(+), 1 deletion(-) create mode 100644 src/Mod/Part/App/Measure.cpp create mode 100644 src/Mod/Part/App/MeasureClient.h create mode 100644 src/Mod/Part/App/MeasureInfo.h diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 62b29c7b0e..23ab086e84 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -185,6 +185,8 @@ #include #include +#include "MeasureClient.h" + namespace Part { extern PyObject* initModule(); @@ -552,10 +554,12 @@ PyMOD_INIT_FUNC(Part) Part::Geom2dOffsetCurve ::init(); Part::Geom2dTrimmedCurve ::init(); + IGESControl_Controller::Init(); STEPControl_Controller::Init(); OCAF::ImportExportSettings::initialize(); - + Part::MeasureClient::initialize(); + PyMOD_Return(partModule); } diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 2c0267a77f..93bf3aa52c 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -545,6 +545,9 @@ SET(Part_SRCS TopoShapeOpCode.h edgecluster.cpp edgecluster.h + MeasureClient.cpp + MeasureClient.h + MeasureInfo.h modelRefine.cpp modelRefine.h Tools.cpp diff --git a/src/Mod/Part/App/Measure.cpp b/src/Mod/Part/App/Measure.cpp new file mode 100644 index 0000000000..e46b01261f --- /dev/null +++ b/src/Mod/Part/App/Measure.cpp @@ -0,0 +1,440 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * 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 * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#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 "VectorAdapter.h" +#include "PartFeature.h" + +#include "Measure.h" + +// From: https://github.com/Celemation/FreeCAD/blob/joel_selection_summary_demo/src/Gui/SelectionSummary.cpp + +// Should work with edges and wires +static float getLength(TopoDS_Shape& wire){ + GProp_GProps gprops; + BRepGProp::LinearProperties(wire, gprops); + return gprops.Mass(); +} + +static float getFaceArea(TopoDS_Shape& face){ + GProp_GProps gprops; + BRepGProp::SurfaceProperties(face, gprops); + return gprops.Mass(); +} + +static float getRadius(TopoDS_Shape& edge){ + // gprops.Mass() would be the circumference (length) of the circle (arc) + if (edge.ShapeType() == TopAbs_EDGE) { + BRepAdaptor_Curve adapt(TopoDS::Edge(edge)); + if (adapt.GetType() != GeomAbs_Circle) { + // TODO: not sure what the error handling here should be. nan? 0.0? + return 0.0; + } + gp_Circ circle = adapt.Circle(); + return circle.Radius(); + } + return 0.0; +} + +TopoDS_Shape getLocatedShape(const std::string& objectName, const std::string& subName) +{ + App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(objectName.c_str()); + // should this be getTopoShape(obj, subName, true)? + Part::TopoShape shape = Part::Feature::getTopoShape(obj); + auto geoFeat = dynamic_cast(obj); + if (geoFeat) { + shape.setPlacement(geoFeat->globalPlacement()); + } + + // Don't get the subShape from datum elements + if (obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { + return shape.getShape(); + } + + if (subName.empty()) + { + return shape.getShape(); + } + return shape.getSubShape(subName.c_str()); +} + + +App::MeasureElementType PartMeasureTypeCb(const char* objectName, const char* subName) { + App::DocumentObject* ob = App::GetApplication().getActiveDocument()->getObject(objectName); +// auto sub = ob->getSubObject(subName); + + TopoDS_Shape shape = Part::Feature::getShape(ob, subName, true); + if (shape.IsNull()) { + // failure here on loading document with existing measurement. + Base::Console().Message("Part::VectorAdapter did not retrieve shape for %s, %s\n", objectName, subName); + return App::MeasureElementType(); + } + TopAbs_ShapeEnum shapeType = shape.ShapeType(); + + switch (shapeType) { + case TopAbs_VERTEX: { + return App::MeasureElementType::POINT; + } + case TopAbs_EDGE: { + const TopoDS_Edge& edge = TopoDS::Edge(shape); + BRepAdaptor_Curve curve(edge); + + switch (curve.GetType()) { + case GeomAbs_Line: { + if (ob->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Datum"))) { + return App::MeasureElementType::LINE; + } + return App::MeasureElementType::LINESEGMENT; + } + case GeomAbs_Circle: { return App::MeasureElementType::CIRCLE; } + case GeomAbs_BezierCurve: + case GeomAbs_BSplineCurve: { + return App::MeasureElementType::CURVE; + } + default: { return App::MeasureElementType::INVALID; } + } + } + case TopAbs_FACE: { + const TopoDS_Face& face = TopoDS::Face(shape); + BRepAdaptor_Surface surface(face); + + switch (surface.GetType()) { + case GeomAbs_Cylinder: { return App::MeasureElementType::CYLINDER; } + case GeomAbs_Plane: { return App::MeasureElementType::PLANE; } + default: { return App::MeasureElementType::INVALID; } + } + } + case TopAbs_SOLID: { + return App::MeasureElementType::Volume; + } + default: { + return App::MeasureElementType::INVALID; + } + } +} + + + +bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub, Base::Matrix4D *mat) +{ + App::Document *docPointer = App::GetApplication().getDocument(doc.c_str()); + if (!docPointer) { + return false; + } + App::DocumentObject *objectPointer = docPointer->getObject(object.c_str()); + if (!objectPointer) { + return false; + } + shapeOut = Part::Feature::getShape(objectPointer,sub.c_str(),true,mat); + return !shapeOut.IsNull(); +} + + + +Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* objectName, const std::string* subName) +{ + (void) objectName; + Base::Matrix4D mat; + TopoDS_Shape shape = Part::Feature::getShape(ob, subName->c_str(), true); + if (shape.IsNull()) { + // failure here on loading document with existing measurement. + Base::Console().Message("Part::VectorAdapter did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + return Part::VectorAdapter(); + } + + TopAbs_ShapeEnum shapeType = shape.ShapeType(); + + + if (shapeType == TopAbs_EDGE) + { + TopoDS_Shape edgeShape; + if (!getShapeFromStrings(edgeShape, ob->getDocument()->getName(), ob->getNameInDocument(), *subName, &mat)) { + return {}; + } + TopoDS_Edge edge = TopoDS::Edge(edgeShape); + // make edge orientation so that end of edge closest to pick is head of vector. + TopoDS_Vertex firstVertex = TopExp::FirstVertex(edge, Standard_True); + TopoDS_Vertex lastVertex = TopExp::LastVertex(edge, Standard_True); + if (firstVertex.IsNull() || lastVertex.IsNull()) { + return {}; + } + gp_Vec firstPoint = Part::VectorAdapter::convert(firstVertex); + gp_Vec lastPoint = Part::VectorAdapter::convert(lastVertex); + Base::Vector3d v(0.0, 0.0, 0.0); //v(current.x,current.y,current.z); + v = mat*v; + gp_Vec pickPoint(v.x, v.y, v.z); + double firstDistance = (firstPoint - pickPoint).Magnitude(); + double lastDistance = (lastPoint - pickPoint).Magnitude(); + if (lastDistance > firstDistance) + { + if (edge.Orientation() == TopAbs_FORWARD) { + edge.Orientation(TopAbs_REVERSED); + } + else { + edge.Orientation(TopAbs_FORWARD); + } + } + return {edge, pickPoint}; + } + if (shapeType == TopAbs_FACE) + { + TopoDS_Shape faceShape; + if (!getShapeFromStrings(faceShape, ob->getDocument()->getName(), ob->getNameInDocument(), *subName, &mat)) { + return {}; + } + + TopoDS_Face face = TopoDS::Face(faceShape); + Base::Vector3d vTemp(0.0, 0.0, 0.0); //v(current.x, current.y, current.z); + vTemp = mat*vTemp; + gp_Vec pickPoint(vTemp.x, vTemp.y, vTemp.z); + return {face, pickPoint}; + } + + return {}; +} + + +Measure::MeasureLengthInfo MeasureLengthHandler(std::string* objectName, std::string* subName){ + TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + + if (shape.IsNull()) { + // failure here on loading document with existing measurement. + Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + return {false, 0.0, Base::Matrix4D()}; + } + TopAbs_ShapeEnum sType = shape.ShapeType(); + + if (sType != TopAbs_EDGE) { + return {false, 0.0, Base::Matrix4D()}; + } + + // Get Center of mass as the attachment point of the label + GProp_GProps gprops; + BRepGProp::LinearProperties(shape, gprops); + auto origin = gprops.CentreOfMass(); + + // Get rotation of line + auto edge = TopoDS::Edge(shape); + ShapeAnalysis_Edge edgeAnalyzer; + gp_Pnt firstPoint = BRep_Tool::Pnt(edgeAnalyzer.FirstVertex(edge)); + gp_Pnt lastPoint = BRep_Tool::Pnt(edgeAnalyzer.LastVertex(edge)); + auto dir = (lastPoint.XYZ() - firstPoint.XYZ()).Normalized(); + Base::Vector3d elementDirection(dir.X(), dir.Y(), dir.Z()); + Base::Vector3d axisUp(0.0, 0.0, 1.0); + Base::Rotation rot(axisUp, elementDirection); + + Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot); + return {true, getLength(shape), placement}; +} + +Measure::MeasureRadiusInfo MeasureRadiusHandler(std::string* objectName, std::string* subName){ + Base::Placement placement; // curve center + orientation + Base::Vector3d pointOnCurve; + + TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + + if (shape.IsNull()) { + return { false, 0.0, pointOnCurve, placement}; + } + TopAbs_ShapeEnum sType = shape.ShapeType(); + + if (sType != TopAbs_EDGE) { + return { false, 0.0, pointOnCurve, placement}; + } + + // Get Center of mass as the attachment point of the label + GProp_GProps gprops; + BRepGProp::LinearProperties(shape, gprops); + auto origin = gprops.CentreOfMass(); + + TopoDS_Edge edge = TopoDS::Edge(shape); + gp_Pnt firstPoint = BRep_Tool::Pnt(TopExp::FirstVertex(edge)); + pointOnCurve = Base::Vector3d(firstPoint.X(), firstPoint.Y(), firstPoint.Z()); + // a somewhat arbitrary radius from center -> point on curve + auto dir = (firstPoint.XYZ() - origin.XYZ()).Normalized(); + Base::Vector3d elementDirection(dir.X(), dir.Y(), dir.Z()); + Base::Vector3d axisUp(0.0, 0.0, 1.0); + Base::Rotation rot(axisUp, elementDirection); + + placement = Base::Placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot); + + return { true, getRadius(shape), pointOnCurve, placement}; +} + + +Measure::MeasureAreaInfo MeasureAreaHandler(std::string* objectName, std::string* subName){ + TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + + if (shape.IsNull()) { + // failure here on loading document with existing measurement. + Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + return {false, 0.0, Base::Matrix4D()}; + } + TopAbs_ShapeEnum sType = shape.ShapeType(); + + if (sType != TopAbs_FACE) { + return {false, 0.0, Base::Matrix4D()}; + } + + // Get Center of mass as the attachment point of the label + GProp_GProps gprops; + BRepGProp::SurfaceProperties(shape, gprops); + auto origin = gprops.CentreOfMass(); + + // TODO: Center of Mass might not lie on the surface, somehow snap to the closest point on the surface? + + Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation()); + return {true, getFaceArea(shape), placement}; +} + + +Base::Vector3d MeasurePositionHandler(std::string* objectName, std::string* subName) { + TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + + if (shape.IsNull()) { + Base::Console().Message("MeasurePositionHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + return Base::Vector3d(); + } + TopAbs_ShapeEnum sType = shape.ShapeType(); + + if (sType != TopAbs_VERTEX) { + return Base::Vector3d(); + } + + TopoDS_Vertex vertex = TopoDS::Vertex(shape); + auto point = BRep_Tool::Pnt(vertex); + return Base::Vector3d(point.X(), point.Y(), point.Z()); +} + + +Measure::MeasureAngleInfo MeasureAngleHandler(std::string* objectName, std::string* subName) { + App::DocumentObject* ob = App::GetApplication().getActiveDocument()->getObject(objectName->c_str()); + TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + if (shape.IsNull()) { + // failure here on loading document with existing measurement. + Base::Console().Message("MeasureAngleHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + return Measure::MeasureAngleInfo(); + } + + TopAbs_ShapeEnum sType = shape.ShapeType(); + + Part::VectorAdapter vAdapt = buildAdapter(ob, objectName, subName); + + gp_Pnt vec; + Base::Vector3d position; + if (sType == TopAbs_FACE) { + TopoDS_Face face = TopoDS::Face(shape); + + GProp_GProps gprops; + BRepGProp::SurfaceProperties(face, gprops); + vec = gprops.CentreOfMass(); + + } else if (sType == TopAbs_EDGE) { + TopoDS_Edge edge = TopoDS::Edge(shape); + + GProp_GProps gprops; + BRepGProp::LinearProperties(edge, gprops); + vec = gprops.CentreOfMass(); + } + + position.Set(vec.X(), vec.Y(), vec.Z()); + + Measure::MeasureAngleInfo info = {vAdapt.isValid(), (Base::Vector3d)vAdapt, position}; + return info; +} + + +Measure::MeasureDistanceInfo MeasureDistanceHandler(std::string* objectName, std::string* subName) { + TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + + if (shape.IsNull()) { + // failure here on loading document with existing measurement. + Base::Console().Message("MeasureDistanceHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + return Measure::MeasureDistanceInfo(); + } + + // Just return the TopoDS_Shape here + return {true, shape}; +} + + +using namespace Measure; + +void Part::Measure::initialize() { + + App::Application& app = App::GetApplication(); + app.addMeasureHandler("Part", PartMeasureTypeCb); + + std::vector proxyList( { "Part", "PartDesign", "Sketcher" } ); + // Extend MeasureLength + MeasureLength::addGeometryHandlers(proxyList, MeasureLengthHandler); + + // Extend MeasurePosition + MeasurePosition::addGeometryHandlers(proxyList, MeasurePositionHandler); + + // Extend MeasureArea + MeasureArea::addGeometryHandlers(proxyList, MeasureAreaHandler); + + // Extend MeasureAngle + MeasureAngle::addGeometryHandlers(proxyList, MeasureAngleHandler); + + // Extend MeasureDistance + MeasureDistance::addGeometryHandlers(proxyList, MeasureDistanceHandler); + + // Extend MeasureRadius + MeasureRadius::addGeometryHandlers(proxyList, MeasureRadiusHandler); +} diff --git a/src/Mod/Part/App/MeasureClient.h b/src/Mod/Part/App/MeasureClient.h new file mode 100644 index 0000000000..80308d0604 --- /dev/null +++ b/src/Mod/Part/App/MeasureClient.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (c) 2023 Wandererfan * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#ifndef PART_MEASURE_H +#define PART_MEASURE_H + +#include + +#include "MeasureInfo.h" + + + +namespace Part +{ + + +class PartExport MeasureClient +{ +public: + + static void initialize(); + + static CallbackRegistrationList reportLengthCB(); + static CallbackRegistrationList reportPositionCB(); + static CallbackRegistrationList reportAreaCB(); + static CallbackRegistrationList reportAngleCB(); + static CallbackRegistrationList reportDistanceCB(); + static CallbackRegistrationList reportRadiusCB(); +}; + + +} //namespace Part + +#endif diff --git a/src/Mod/Part/App/MeasureInfo.h b/src/Mod/Part/App/MeasureInfo.h new file mode 100644 index 0000000000..6af72a962e --- /dev/null +++ b/src/Mod/Part/App/MeasureInfo.h @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: LGPL-2.0-or-later + +/*************************************************************************** + * Copyright (c) 2024 wandererfan * + * * + * 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 * + * . * + * * + **************************************************************************/ + +//! MeasureInfo.h +//! ancestor class and the various flavours of MeasureXXXXXInfo. + +#ifndef PART_MEASUREINFO_H +#define PART_MEASUREINFO_H + +#include + +#include +#include +#include +#include + +#include +#include +#include + +class TopoDS_Shape; + +namespace Part { + +class MeasureInfo; +using MeasureInfoPtr = std::shared_ptr; +class MeasureAngleInfo; +using MeasureAngleInfoPtr = std::shared_ptr; +class MeasureAreaInfo; +using MeasureAreaInfoPtr = std::shared_ptr; +class MeasureDistanceInfo; +using MeasureDistanceInfoPtr = std::shared_ptr; +class MeasureLengthInfo; +using MeasureLengthInfoPtr = std::shared_ptr; +class MeasurePositionInfo; +using MeasurePositionInfoPtr = std::shared_ptr; +class MeasureRadiusInfo; +using MeasureRadiusInfoPtr = std::shared_ptr; + + +class PartExport MeasureInfo { +public: + // making the destructor virtual so MeasureInfo is polymorphic + MeasureInfo() = default; + MeasureInfo(bool val) : valid(val) {} + virtual ~MeasureInfo() = default; + bool valid{false}; +}; + +class PartExport MeasureAngleInfo : public MeasureInfo { +public: + MeasureAngleInfo() = default; + MeasureAngleInfo(bool val, Base::Vector3d orient, Base::Vector3d pos) : + MeasureInfo(val), orientation(orient), position(pos) {} + ~MeasureAngleInfo() override = default; + + Base::Vector3d orientation{0.0, 0.0, 0.0}; + Base::Vector3d position{0.0, 0.0, 0.0}; +}; + +class PartExport MeasureAreaInfo : public MeasureInfo { +public: + MeasureAreaInfo() = default; + MeasureAreaInfo(bool val, double a2, Base::Placement plm) : + MeasureInfo(val), area(a2), placement(plm) {} + ~MeasureAreaInfo() override = default; + + double area{0}; + Base::Placement placement{}; +}; + +// Translate geometry reference into an OCC type +class PartExport MeasureDistanceInfo : public MeasureInfo { +public: + MeasureDistanceInfo() = default; + explicit MeasureDistanceInfo(bool val, const TopoDS_Shape* shp) : + MeasureInfo(val), shape(shp) {} + ~MeasureDistanceInfo() override = default; + + const TopoDS_Shape* getShape() { return shape; } + +private: + const TopoDS_Shape* shape{nullptr}; +}; + +class PartExport MeasureLengthInfo : public MeasureInfo { +public: + MeasureLengthInfo() = default; + MeasureLengthInfo(bool val, double len, Base::Placement plm) : + MeasureInfo(val), length(len), placement(plm) {} + ~MeasureLengthInfo() override = default; + + double length{0}; + Base::Placement placement{}; +}; + +class PartExport MeasurePositionInfo : public MeasureInfo { +public: + MeasurePositionInfo() = default; + MeasurePositionInfo(bool val, Base::Vector3d pos) : + MeasureInfo(val), position(pos) {} + ~MeasurePositionInfo() override = default; + + Base::Vector3d position{0.0, 0.0, 0.0}; +}; + +class PartExport MeasureRadiusInfo : public MeasureInfo { +public: + MeasureRadiusInfo() = default; + MeasureRadiusInfo(bool val, double rad, Base::Vector3d point, Base::Placement plm) : + MeasureInfo(val), radius(rad), pointOnCurve(point), placement(plm) {} + ~MeasureRadiusInfo() override = default; + + double radius{}; + Base::Vector3d pointOnCurve; + Base::Placement placement; // curve center & circle orientation +}; + +//! callback registrations +// TODO: is there more that one place that GeometryHandler is defined? + using GeometryHandler = std::function; + +class PartExport CallbackRegistrationRecord +{ +public: + CallbackRegistrationRecord() = default; + CallbackRegistrationRecord(const std::string& module, const std::string& measureType, GeometryHandler callback) : + m_module(module), m_measureType(measureType), m_callback(callback) + { } + + std::string m_module; + std::string m_measureType; + GeometryHandler m_callback; +}; + +using CallbackRegistrationList = std::vector; + +} //end namespace Part + +#endif