Core / Measure: Introduce QuickMeasure

This commit is contained in:
PaddleStroke
2024-04-30 17:10:25 +02:00
committed by Yorik van Havre
parent 0262d56e89
commit e5780a5e3d
14 changed files with 882 additions and 142 deletions

View File

@@ -269,6 +269,7 @@ struct MainWindowP
{
DimensionWidget* sizeLabel;
QLabel* actionLabel;
QLabel* rightSideLabel;
QTimer* actionTimer;
QTimer* statusTimer;
QTimer* activityTimer;
@@ -445,6 +446,7 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
// labels and progressbar
d->status = new StatusBarObserver();
d->actionLabel = new QLabel(statusBar());
d->actionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
// d->actionLabel->setMinimumWidth(120);
d->sizeLabel = new DimensionWidget(statusBar());
@@ -454,6 +456,10 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
statusBar()->addPermanentWidget(progressBar, 0);
statusBar()->addPermanentWidget(d->sizeLabel, 0);
d->rightSideLabel = new QLabel(statusBar());
d->rightSideLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
statusBar()->addPermanentWidget(d->rightSideLabel);
auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/NotificationArea");
auto notificationAreaEnabled = hGrp->GetBool("NotificationAreaEnabled", true);
@@ -464,6 +470,7 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
notificationArea->setStyleSheet(QStringLiteral("text-align:left;"));
statusBar()->addPermanentWidget(notificationArea);
}
// clears the action label
d->actionTimer = new QTimer( this );
d->actionTimer->setObjectName(QString::fromLatin1("actionTimer"));
@@ -2431,6 +2438,11 @@ void MainWindow::showMessage(const QString& message, int timeout) {
d->actionTimer->stop();
}
void MainWindow::setRightSideMessage(const QString& message)
{
d->rightSideLabel->setText(message.simplified());
}
void MainWindow::showStatus(int type, const QString& message)
{
if(QApplication::instance()->thread() != QThread::currentThread()) {

View File

@@ -265,6 +265,7 @@ public Q_SLOTS:
void statusMessageChanged();
void showMessage (const QString & message, int timeout = 0);
void setRightSideMessage(const QString & message);
// Set main window title
void setWindowTitle(const QString& string);

View File

@@ -24,16 +24,22 @@
#ifndef _PreComp_
# include <BRep_Tool.hxx>
# include <BRepAdaptor_Curve.hxx>
# include <BRepAdaptor_Surface.hxx>
# include <BRepExtrema_DistShapeShape.hxx>
# include <BRepGProp.hxx>
# include <GCPnts_AbscissaPoint.hxx>
# include <gp_Pln.hxx>
# include <gp_Circ.hxx>
# include <gp_Torus.hxx>
# include <gp_Cylinder.hxx>
# include <gp_Sphere.hxx>
# include <gp_Lin.hxx>
# include <GProp_GProps.hxx>
# include <TopoDS.hxx>
# include <TopoDS_Shape.hxx>
#endif
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Mod/Part/App/PartFeature.h>
@@ -55,7 +61,7 @@ TYPESYSTEM_SOURCE(Measure::Measurement, Base::BaseClass)
Measurement::Measurement()
{
measureType = Invalid;
measureType = MeasureType::Invalid;
References3D.setScope(App::LinkScope::Global);
}
@@ -66,7 +72,7 @@ void Measurement::clear()
std::vector<App::DocumentObject*> Objects;
std::vector<std::string> SubElements;
References3D.setValues(Objects, SubElements);
measureType = Invalid;
measureType = MeasureType::Invalid;
}
bool Measurement::has3DReferences()
@@ -91,11 +97,11 @@ int Measurement::addReference3D(App::DocumentObject *obj, const char* subName)
References3D.setValues(objects, subElements);
measureType = getType();
measureType = findType();
return References3D.getSize();
}
MeasureType Measurement::getType()
MeasureType Measurement::findType()
{
const std::vector<App::DocumentObject*> &objects = References3D.getValues();
const std::vector<std::string> &subElements = References3D.getSubValues();
@@ -107,7 +113,14 @@ MeasureType Measurement::getType()
int verts = 0;
int edges = 0;
int lines = 0;
int circles = 0;
int faces = 0;
int planes = 0;
int cylinders = 0;
int cones = 0;
int torus = 0;
int spheres = 0;
int vols = 0;
for (;obj != objects.end(); ++obj, ++subEl) {
@@ -115,13 +128,15 @@ MeasureType Measurement::getType()
// Check if solid object
if(strcmp((*subEl).c_str(), "") == 0) {
vols++;
} else {
}
else {
TopoDS_Shape refSubShape;
try {
refSubShape = Part::Feature::getShape(*obj,(*subEl).c_str(),true);
if(refSubShape.IsNull())
return Invalid;
if(refSubShape.IsNull()){
return MeasureType::Invalid;
}
}
catch (Standard_Failure& e) {
std::stringstream errorMsg;
@@ -139,11 +154,38 @@ MeasureType Measurement::getType()
case TopAbs_EDGE:
{
edges++;
TopoDS_Edge edge = TopoDS::Edge(refSubShape);
BRepAdaptor_Curve sf(edge);
if (sf.GetType() == GeomAbs_Line) {
lines++;
}
else if (sf.GetType() == GeomAbs_Circle) {
circles++;
}
}
break;
case TopAbs_FACE:
{
faces++;
TopoDS_Face face = TopoDS::Face(refSubShape);
BRepAdaptor_Surface sf(face);
if (sf.GetType() == GeomAbs_Plane) {
planes++;
}
else if (sf.GetType() == GeomAbs_Cylinder) {
cylinders++;
}
else if (sf.GetType() == GeomAbs_Sphere) {
spheres++;
}
else if (sf.GetType() == GeomAbs_Cone) {
cones++;
}
else if (sf.GetType() == GeomAbs_Torus) {
torus++;
}
}
break;
default:
@@ -154,46 +196,105 @@ MeasureType Measurement::getType()
if(vols > 0) {
if(verts > 0 || edges > 0 || faces > 0) {
mode = Invalid;
} else {
mode = Volumes;
mode = MeasureType::Invalid;
}
} else if(faces > 0) {
else {
mode = MeasureType::Volumes;
}
}
else if(faces > 0) {
if(verts > 0 || edges > 0) {
if(faces > 1 && verts > 1 && edges > 0) {
mode = Invalid;
} else {
// One Surface and One Point
mode = PointToSurface;
if (faces == 1 && verts == 1) {
mode = MeasureType::PointToSurface;
}
else {
mode = MeasureType::Invalid;
}
} else {
mode = Surfaces;
}
} else if(edges > 0) {
else {
if (planes == 1 && faces == 1) {
mode = MeasureType::Plane;
}
else if (planes == 2 && faces == 2) {
if (planesAreParallel()) {
mode = MeasureType::TwoPlanes;
}
else {
mode = MeasureType::Surfaces;
}
}
else if (cylinders == 1 && faces == 1) {
mode = MeasureType::Cylinder;
}
else if (cones == 1 && faces == 1) {
mode = MeasureType::Cone;
}
else if (spheres == 1 && faces == 1) {
mode = MeasureType::Sphere;
}
else if (torus == 1 && faces == 1) {
mode = MeasureType::Torus;
}
else {
mode = MeasureType::Surfaces;
}
}
}
else if(edges > 0) {
if(verts > 0) {
if(verts > 1 && edges > 0) {
mode = Invalid;
} else {
mode = PointToEdge;
mode = MeasureType::Invalid;
}
else {
mode = MeasureType::PointToEdge;
}
} else {
mode = Edges;
}
} else if (verts > 0) {
mode = Points;
} else {
mode = Invalid;
else if (lines == 1 && edges == 1) {
mode = MeasureType::Line;
}
else if (lines == 2 && edges == 2) {
if (linesAreParallel()) {
mode = MeasureType::TwoParallelLines;
}
else {
mode = MeasureType::TwoLines;
}
}
else if (circles == 1 && edges == 1) {
mode = MeasureType::Circle;
}
else {
mode = MeasureType::Edges;
}
}
else if (verts > 0) {
if (verts == 2) {
mode = MeasureType::PointToPoint;
}
else {
mode = MeasureType::Points;
}
}
else {
mode = MeasureType::Invalid;
}
return mode;
}
MeasureType Measurement::getType()
{
return measureType;
}
TopoDS_Shape Measurement::getShape(App::DocumentObject *obj , const char *subName) const
{
// Base::Console().Message("Meas::getShape(%s, %s)\n", obj->getNameInDocument(), subName);
//temporary fix to get "Vertex7" from "Body003.Pocket020.Vertex7"
//when selected, Body features are provided as featureName and subNameAndIndex
//other sources provide the full extended name with index
if (strcmp(subName, "") == 0) {
return Part::Feature::getShape(obj);
}
std::string workingSubName(subName);
size_t lastDot = workingSubName.rfind('.');
if (lastDot != std::string::npos) {
@@ -229,20 +330,24 @@ double Measurement::length() const
int numRefs = References3D.getSize();
if(numRefs == 0) {
Base::Console().Error("Measurement::length - No 3D references available\n");
} else if (measureType == Invalid) {
}
else if (measureType == MeasureType::Invalid) {
Base::Console().Error("Measurement::length - measureType is Invalid\n");
} else {
}
else {
const std::vector<App::DocumentObject*> &objects = References3D.getValues();
const std::vector<std::string> &subElements = References3D.getSubValues();
if(measureType == Points ||
measureType == PointToEdge ||
measureType == PointToSurface) {
if(measureType == MeasureType::Points ||
measureType == MeasureType::PointToPoint ||
measureType == MeasureType::PointToEdge ||
measureType == MeasureType::PointToSurface) {
Base::Vector3d diff = this->delta();
//return diff.Length();
result = diff.Length();
} else if(measureType == Edges) {
}
else if(measureType == MeasureType::Edges || measureType == MeasureType::Line
|| measureType == MeasureType::TwoLines || measureType == MeasureType::Circle) {
// Iterate through edges and calculate each length
std::vector<App::DocumentObject*>::const_iterator obj = objects.begin();
@@ -287,11 +392,99 @@ double Measurement::length() const
}
} //end switch
} //end for
} //end measureType == Edges
}
}
return result;
}
double Measurement::lineLineDistance() const
{
// We don't use delta() because BRepExtrema_DistShapeShape return minimum length between line segment.
// Here we get the nominal distance between the infinite lines.
double distance = 0.0;
if (measureType != MeasureType::TwoParallelLines || References3D.getSize() != 2) {
return distance;
}
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
const std::vector<std::string>& subElements = References3D.getSubValues();
// Get the first line
TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str());
const TopoDS_Edge& edge1 = TopoDS::Edge(shape1);
BRepAdaptor_Curve curve1(edge1);
// Get the second line
TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str());
const TopoDS_Edge& edge2 = TopoDS::Edge(shape2);
BRepAdaptor_Curve curve2(edge2);
if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) {
gp_Lin line1 = curve1.Line();
gp_Lin line2 = curve2.Line();
gp_Pnt p1 = line1.Location();
gp_Pnt p2 = line2.Location();
// Create a vector from a point on line1 to a point on line2
gp_Vec lineVec(p1, p2);
// The direction vector of one of the lines
gp_Dir lineDir = line1.Direction();
// Project lineVec onto lineDir
gp_Vec parallelComponent = lineVec.Dot(lineDir) * lineDir;
// Compute the perpendicular component
gp_Vec perpendicularComponent = lineVec - parallelComponent;
// Distance is the magnitude of the perpendicular component
distance = perpendicularComponent.Magnitude();
}
else {
Base::Console().Error("Measurement::length - TwoLines measureType requires two lines\n");
}
return distance;
}
double Measurement::planePlaneDistance() const {
if (measureType != MeasureType::TwoPlanes || References3D.getSize() != 2) {
return 0.0;
}
const auto& objects = References3D.getValues();
const auto& subElements = References3D.getSubValues();
std::vector<gp_Pln> planes;
// Get the first plane
TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str());
const TopoDS_Face& face1 = TopoDS::Face(shape1);
BRepAdaptor_Surface surface1(face1);
const gp_Pln& plane1 = surface1.Plane();
// Get the second plane
TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str());
const TopoDS_Face& face2 = TopoDS::Face(shape2);
BRepAdaptor_Surface surface2(face2);
const gp_Pln& plane2 = surface2.Plane();
// Distance between two parallel planes
gp_Pnt pointOnPlane1 = plane1.Location();
gp_Dir normalToPlane1 = plane1.Axis().Direction();
gp_Pnt pointOnPlane2 = plane2.Location();
// Create a vector from a point on plane1 to a point on plane2
gp_Vec vectorBetweenPlanes(pointOnPlane1, pointOnPlane2);
// Project this vector onto the plane normal
double distance = Abs(vectorBetweenPlanes.Dot(normalToPlane1));
return distance;
}
double Measurement::angle(const Base::Vector3d & /*param*/) const
{
//TODO: do these references arrive as obj+sub pairs or as a struct of obj + [subs]?
@@ -300,89 +493,107 @@ double Measurement::angle(const Base::Vector3d & /*param*/) const
int numRefs = objects.size();
if(numRefs == 0) {
throw Base::RuntimeError("No references available for angle measurement");
} else if (measureType == Invalid) {
}
else if (measureType == MeasureType::Invalid) {
throw Base::RuntimeError("MeasureType is Invalid for angle measurement");
} else {
if(measureType == Edges) {
//Only case that is supported is edge to edge
//The angle between two skew lines is measured by the angle between one line (A)
//and a line (B) with the direction of the second through a point on the first line.
//Since we don't know if the directions of the lines point in the same general direction
//we could get the angle we want or the supplementary angle.
if(numRefs == 2) {
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
}
else if(measureType == MeasureType::TwoLines) {
//Only case that is supported is edge to edge
//The angle between two skew lines is measured by the angle between one line (A)
//and a line (B) with the direction of the second through a point on the first line.
//Since we don't know if the directions of the lines point in the same general direction
//we could get the angle we want or the supplementary angle.
if(numRefs == 2) {
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
BRepAdaptor_Curve curve1(TopoDS::Edge(shape1));
BRepAdaptor_Curve curve2(TopoDS::Edge(shape2));
BRepAdaptor_Curve curve1(TopoDS::Edge(shape1));
BRepAdaptor_Curve curve2(TopoDS::Edge(shape2));
if(curve1.GetType() == GeomAbs_Line &&
curve2.GetType() == GeomAbs_Line) {
if(curve1.GetType() == GeomAbs_Line &&
curve2.GetType() == GeomAbs_Line) {
gp_Pnt pnt1First = curve1.Value(curve1.FirstParameter());
gp_Dir dir1 = curve1.Line().Direction();
gp_Dir dir2 = curve2.Line().Direction();
gp_Dir dir2r = curve2.Line().Direction().Reversed();
gp_Pnt pnt1First = curve1.Value(curve1.FirstParameter());
gp_Dir dir1 = curve1.Line().Direction();
gp_Dir dir2 = curve2.Line().Direction();
gp_Dir dir2r = curve2.Line().Direction().Reversed();
gp_Lin l1 = gp_Lin(pnt1First, dir1); // (A)
gp_Lin l2 = gp_Lin(pnt1First, dir2); // (B)
gp_Lin l2r = gp_Lin(pnt1First, dir2r); // (B')
Standard_Real aRad = l1.Angle(l2);
double aRadr = l1.Angle(l2r);
return std::min(aRad, aRadr) * 180 / M_PI;
} else {
throw Base::RuntimeError("Measurement references must both be lines");
}
} else {
throw Base::RuntimeError("Can not compute angle measurement - too many references");
gp_Lin l1 = gp_Lin(pnt1First, dir1); // (A)
gp_Lin l2 = gp_Lin(pnt1First, dir2); // (B)
gp_Lin l2r = gp_Lin(pnt1First, dir2r); // (B')
Standard_Real aRad = l1.Angle(l2);
double aRadr = l1.Angle(l2r);
return std::min(aRad, aRadr) * 180 / M_PI;
}
} else if (measureType == Points) {
//NOTE: we are calculating the 3d angle here, not the projected angle
//ASSUMPTION: the references are in end-apex-end order
if(numRefs == 3) {
TopoDS_Shape shape0 = getShape(objects.at(0), subElements.at(0).c_str());
TopoDS_Shape shape1 = getShape(objects.at(1), subElements.at(1).c_str());
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(2).c_str());
if (shape0.ShapeType() != TopAbs_VERTEX ||
shape1.ShapeType() != TopAbs_VERTEX ||
shape2.ShapeType() != TopAbs_VERTEX) {
throw Base::RuntimeError("Measurement references for 3 point angle are not Vertex");
}
gp_Pnt gEnd0 = BRep_Tool::Pnt(TopoDS::Vertex(shape0));
gp_Pnt gApex = BRep_Tool::Pnt(TopoDS::Vertex(shape1));
gp_Pnt gEnd1 = BRep_Tool::Pnt(TopoDS::Vertex(shape2));
gp_Dir gDir0 = gp_Dir(gEnd0.XYZ() - gApex.XYZ());
gp_Dir gDir1 = gp_Dir(gEnd1.XYZ() - gApex.XYZ());
gp_Lin line0 = gp_Lin(gEnd0, gDir0);
gp_Lin line1 = gp_Lin(gEnd1, gDir1);
double radians = line0.Angle(line1);
return radians * 180 / M_PI;
else {
throw Base::RuntimeError("Measurement references must both be lines");
}
}
else {
throw Base::RuntimeError("Can not compute angle measurement - too many references");
}
}
else if (measureType == MeasureType::Points) {
//NOTE: we are calculating the 3d angle here, not the projected angle
//ASSUMPTION: the references are in end-apex-end order
if(numRefs == 3) {
TopoDS_Shape shape0 = getShape(objects.at(0), subElements.at(0).c_str());
TopoDS_Shape shape1 = getShape(objects.at(1), subElements.at(1).c_str());
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(2).c_str());
if (shape0.ShapeType() != TopAbs_VERTEX ||
shape1.ShapeType() != TopAbs_VERTEX ||
shape2.ShapeType() != TopAbs_VERTEX) {
throw Base::RuntimeError("Measurement references for 3 point angle are not Vertex");
}
gp_Pnt gEnd0 = BRep_Tool::Pnt(TopoDS::Vertex(shape0));
gp_Pnt gApex = BRep_Tool::Pnt(TopoDS::Vertex(shape1));
gp_Pnt gEnd1 = BRep_Tool::Pnt(TopoDS::Vertex(shape2));
gp_Dir gDir0 = gp_Dir(gEnd0.XYZ() - gApex.XYZ());
gp_Dir gDir1 = gp_Dir(gEnd1.XYZ() - gApex.XYZ());
gp_Lin line0 = gp_Lin(gEnd0, gDir0);
gp_Lin line1 = gp_Lin(gEnd1, gDir1);
double radians = line0.Angle(line1);
return radians * 180 / M_PI;
}
}
throw Base::RuntimeError("Unexpected error for angle measurement");
}
double Measurement::radius() const
{
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
const std::vector<std::string>& subElements = References3D.getSubValues();
int numRefs = References3D.getSize();
if(numRefs == 0) {
throw Base::RuntimeError("Measurement - radius - No References3D provided");
} else {
if(numRefs == 1 || measureType == Edges) {
const std::vector<App::DocumentObject*> &objects = References3D.getValues();
const std::vector<std::string> &subElements = References3D.getSubValues();
Base::Console().Error("Measurement::radius - No 3D references available\n");
}
else if (measureType == MeasureType::Circle) {
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
const TopoDS_Edge& edge = TopoDS::Edge(shape);
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
const TopoDS_Edge& edge = TopoDS::Edge(shape);
BRepAdaptor_Curve curve(edge);
if(curve.GetType() == GeomAbs_Circle) {
return (double) curve.Circle().Radius();
}
BRepAdaptor_Curve curve(edge);
if(curve.GetType() == GeomAbs_Circle) {
return (double) curve.Circle().Radius();
}
}
throw Base::RuntimeError("Measurement - radius - Invalid References3D Provided");
else if (measureType == MeasureType::Cylinder || measureType == MeasureType::Sphere || measureType == MeasureType::Torus) {
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
TopoDS_Face face = TopoDS::Face(shape);
BRepAdaptor_Surface sf(face);
if (sf.GetType() == GeomAbs_Cylinder) {
return sf.Cylinder().Radius();
}
else if (sf.GetType() == GeomAbs_Sphere) {
return sf.Sphere().Radius();
}
else if (sf.GetType() == GeomAbs_Torus) {
return sf.Torus().MinorRadius();
}
}
Base::Console().Error("Measurement::radius - Invalid References3D Provided\n");
return 0.0;
}
Base::Vector3d Measurement::delta() const
@@ -391,13 +602,15 @@ Base::Vector3d Measurement::delta() const
int numRefs = References3D.getSize();
if (numRefs == 0) {
Base::Console().Error("Measurement::delta - No 3D references available\n");
} else if (measureType == Invalid) {
}
else if (measureType == MeasureType::Invalid) {
Base::Console().Error("Measurement::delta - measureType is Invalid\n");
} else {
}
else {
const std::vector<App::DocumentObject*> &objects = References3D.getValues();
const std::vector<std::string> &subElements = References3D.getSubValues();
if(measureType == Points) {
if(measureType == MeasureType::PointToPoint) {
if(numRefs == 2) {
// Keep separate case for two points to reduce need for complex algorithm
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
@@ -411,8 +624,8 @@ Base::Vector3d Measurement::delta() const
gp_XYZ diff = P2.XYZ() - P1.XYZ();
return Base::Vector3d(diff.X(), diff.Y(), diff.Z());
}
} else if(measureType == PointToEdge ||
measureType == PointToSurface) {
}
else if(measureType == MeasureType::PointToEdge || measureType == MeasureType::PointToSurface) {
// BrepExtema can calculate minimum distance between any set of topology sets.
if(numRefs == 2) {
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
@@ -427,10 +640,10 @@ Base::Vector3d Measurement::delta() const
gp_Pnt P2 = extrema.PointOnShape2(1);
gp_XYZ diff = P2.XYZ() - P1.XYZ();
result = Base::Vector3d(diff.X(), diff.Y(), diff.Z());
// return Base::Vector3d(diff.X(), diff.Y(), diff.Z());
}
}
} else if(measureType == Edges) {
}
else if(measureType == MeasureType::Edges) {
// Only case that is supported is straight line edge
if(numRefs == 1) {
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
@@ -442,9 +655,9 @@ Base::Vector3d Measurement::delta() const
gp_Pnt P2 = curve.Value(curve.LastParameter());
gp_XYZ diff = P2.XYZ() - P1.XYZ();
result = Base::Vector3d(diff.X(), diff.Y(), diff.Z());
// return Base::Vector3d(diff.X(), diff.Y(), diff.Z());
}
} else if(numRefs == 2) {
}
else if(numRefs == 2) {
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
@@ -463,18 +676,64 @@ Base::Vector3d Measurement::delta() const
gp_Pnt P2 = extrema.PointOnShape2(1);
gp_XYZ diff = P2.XYZ() - P1.XYZ();
result = Base::Vector3d(diff.X(), diff.Y(), diff.Z());
// return Base::Vector3d(diff.X(), diff.Y(), diff.Z());
}
}
}
} else {
}
else {
Base::Console().Error("Measurement::delta - measureType is not recognized\n");
}
}
// throw Base::ValueError("An invalid selection was made");
return result;
}
double Measurement::volume() const
{
double result = 0.0;
if (References3D.getSize() == 0) {
Base::Console().Error("Measurement::volume - No 3D references available\n");
}
else if (measureType != MeasureType::Volumes) {
Base::Console().Error("Measurement::volume - measureType is not Volumes\n");
}
else {
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
const std::vector<std::string>& subElements = References3D.getSubValues();
for (size_t i = 0; i < objects.size(); ++i) {
GProp_GProps props = GProp_GProps();
BRepGProp::VolumeProperties(getShape(objects[i], subElements[i].c_str()), props);
result += props.Mass();
}
}
return result;
}
double Measurement::area() const
{
double result = 0.0;
if (References3D.getSize() == 0) {
Base::Console().Error("Measurement::area - No 3D references available\n");
}
else if (measureType == MeasureType::Volumes || measureType == MeasureType::Surfaces
|| measureType == MeasureType::Cylinder || measureType == MeasureType::Cone
|| measureType == MeasureType::Sphere || measureType == MeasureType::Torus
|| measureType == MeasureType::Plane) {
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
const std::vector<std::string>& subElements = References3D.getSubValues();
for (size_t i = 0; i < objects.size(); ++i) {
GProp_GProps props;
BRepGProp::SurfaceProperties(getShape(objects[i], subElements[i].c_str()), props);
result += props.Mass(); // Area is obtained using Mass method for surface properties
}
}
else {
Base::Console().Error("Measurement::area - measureType is not valid\n");
}
return result;
}
Base::Vector3d Measurement::massCenter() const
{
@@ -482,14 +741,16 @@ Base::Vector3d Measurement::massCenter() const
int numRefs = References3D.getSize();
if (numRefs == 0) {
Base::Console().Error("Measurement::massCenter - No 3D references available\n");
} else if (measureType == Invalid) {
}
else if (measureType == MeasureType::Invalid) {
Base::Console().Error("Measurement::massCenter - measureType is Invalid\n");
} else {
}
else {
const std::vector<App::DocumentObject*> &objects = References3D.getValues();
const std::vector<std::string> &subElements = References3D.getSubValues();
GProp_GProps gprops = GProp_GProps();
if(measureType == Volumes) {
if(measureType == MeasureType::Volumes) {
// Iterate through edges and calculate each length
std::vector<App::DocumentObject*>::const_iterator obj = objects.begin();
std::vector<std::string>::const_iterator subEl = subElements.begin();
@@ -510,7 +771,8 @@ Base::Vector3d Measurement::massCenter() const
gp_Pnt cog = gprops.CentreOfMass();
return Base::Vector3d(cog.X(), cog.Y(), cog.Z());
} else {
}
else {
Base::Console().Error("Measurement::massCenter - measureType is not recognized\n");
// throw Base::ValueError("Measurement - massCenter - Invalid References3D Provided");
}
@@ -518,6 +780,83 @@ Base::Vector3d Measurement::massCenter() const
return result;
}
bool Measurement::planesAreParallel() const {
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
const std::vector<std::string>& subElements = References3D.getSubValues();
std::vector<gp_Dir> planeNormals;
for (size_t i = 0; i < objects.size(); ++i) {
TopoDS_Shape refSubShape;
try {
refSubShape = Part::Feature::getShape(objects[i], subElements[i].c_str(), true);
if (refSubShape.IsNull()) {
return false;
}
}
catch (Standard_Failure& e) {
std::stringstream errorMsg;
errorMsg << "Measurement - planesAreParallel - " << e.GetMessageString() << std::endl;
throw Base::CADKernelError(e.GetMessageString());
}
if (refSubShape.ShapeType() == TopAbs_FACE) {
TopoDS_Face face = TopoDS::Face(refSubShape);
BRepAdaptor_Surface sf(face);
if (sf.GetType() == GeomAbs_Plane) {
gp_Pln plane = sf.Plane();
gp_Dir normal = plane.Axis().Direction();
planeNormals.push_back(normal);
}
}
}
if (planeNormals.size() != 2) {
return false; // Ensure exactly two planes are considered
}
// Check if normals are parallel (either identical or opposite)
const gp_Dir& normal1 = planeNormals[0];
const gp_Dir& normal2 = planeNormals[1];
return normal1.IsParallel(normal2, Precision::Angular());
}
bool Measurement::linesAreParallel() const {
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
const std::vector<std::string>& subElements = References3D.getSubValues();
if (References3D.getSize() != 2) {
return false;
}
// Get the first line
TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str());
const TopoDS_Edge& edge1 = TopoDS::Edge(shape1);
BRepAdaptor_Curve curve1(edge1);
// Get the second line
TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str());
const TopoDS_Edge& edge2 = TopoDS::Edge(shape2);
BRepAdaptor_Curve curve2(edge2);
if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) {
gp_Lin line1 = curve1.Line();
gp_Lin line2 = curve2.Line();
gp_Dir dir1 = line1.Direction();
gp_Dir dir2 = line2.Direction();
// Check if lines are parallel
if (dir1.IsParallel(dir2, Precision::Angular())) {
return true;
}
}
return false;
}
unsigned int Measurement::getMemSize() const
{
return 0;

View File

@@ -36,10 +36,20 @@
class TopoDS_Shape;
namespace Measure
{
enum MeasureType {
enum class MeasureType {
Volumes, // Measure the Volume(s)
Edges, // Measure the Edge(s)
Line, // One Line
TwoLines, // Two lines
TwoParallelLines, // Two parallel lines
Circle, // One circle
Surfaces, // Measure the surface(s)
Cylinder, // One Cylinder
Cone, // One Cone
Sphere, // One Sphere
Torus, // One Torus
Plane, // One Plane
TwoPlanes, // One Plane
Points,
PointToPoint, // Measure between TWO points
PointToEdge, // Measure between ONE point and ONE edge
@@ -66,6 +76,7 @@ public:
int addReference3D(App::DocumentObject* obj, const char *subName);
MeasureType getType();
MeasureType findType();
// from base class
PyObject *getPyObject() override;
@@ -74,6 +85,8 @@ public:
// Methods for distances (edge length, two points, edge and a point
double length() const;
Base::Vector3d delta() const; //when would client use delta??
double lineLineDistance() const;
double planePlaneDistance() const;
// Calculates the radius for an arc or circular edge
double radius() const;
@@ -81,11 +94,20 @@ public:
// Calculates the angle between two edges
double angle(const Base::Vector3d &param = Base::Vector3d(0,0,0)) const; //param is never used???
// Calculate volumetric/mass properties
// Calculate the center of mass
Base::Vector3d massCenter() const;
// Calculate the volume of selected volumes
double volume() const;
// Calculate the area of selection
double area() const;
static Base::Vector3d toVector3d(const gp_Pnt gp) { return Base::Vector3d(gp.X(), gp.Y(), gp.Z()); }
bool planesAreParallel() const;
bool linesAreParallel() const;
protected:
TopoDS_Shape getShape(App::DocumentObject *obj , const char *subName) const;

View File

@@ -40,6 +40,26 @@
<UserDocu>measure the length of the references</UserDocu>
</Documentation>
</Methode>
<Methode Name="volume">
<Documentation>
<UserDocu>measure the volume of the references</UserDocu>
</Documentation>
</Methode>
<Methode Name="area">
<Documentation>
<UserDocu>measure the area of the references</UserDocu>
</Documentation>
</Methode>
<Methode Name="lineLineDistance">
<Documentation>
<UserDocu>measure the line-Line Distance of the references. Returns 0 if references are not 2 lines.</UserDocu>
</Documentation>
</Methode>
<Methode Name="planePlaneDistance">
<Documentation>
<UserDocu>measure the plane-plane distance of the references. Returns 0 if references are not 2 planes.</UserDocu>
</Documentation>
</Methode>
<Methode Name="angle">
<Documentation>
<UserDocu>measure the angle between two edges</UserDocu>

View File

@@ -126,6 +126,50 @@ PyObject* MeasurementPy::length(PyObject *args)
return Py::new_reference_to(length);
}
PyObject* MeasurementPy::lineLineDistance(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
Py::Float length;
length = this->getMeasurementPtr()->lineLineDistance();
return Py::new_reference_to(length);
}
PyObject* MeasurementPy::planePlaneDistance(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
Py::Float length;
length = this->getMeasurementPtr()->planePlaneDistance();
return Py::new_reference_to(length);
}
PyObject* MeasurementPy::volume(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
Py::Float length;
length = this->getMeasurementPtr()->volume();
return Py::new_reference_to(length);
}
PyObject* MeasurementPy::area(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
Py::Float length;
length = this->getMeasurementPtr()->area();
return Py::new_reference_to(length);
}
PyObject* MeasurementPy::radius(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))

View File

@@ -20,36 +20,30 @@
* *
***************************************************************************/
#ifndef __PRECOMPILED__
#define __PRECOMPILED__
#ifndef MEASUREGUI_PRECOMPILED_H
#define MEASUREGUI_PRECOMPILED_H
#include <FCConfig.h>
#ifdef FC_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
# define NOMINMAX
#endif
#endif
#include <Mod/Measure/MeasureGlobal.h>
#ifdef _PreComp_
// standard
#include <cfloat>
#include <cmath>
// STL
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <BRep_Tool.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepExtrema_DistShapeShape.hxx>
#include <BRepGProp.hxx>
#include <GCPnts_AbscissaPoint.hxx>
#include <gp_Circ.hxx>
#include <gp_Lin.hxx>
#include <GProp_GProps.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
// OpenCasCade
#include <Mod/Part/App/OpenCascadeAll.h>
#elif defined(FC_OS_WIN32)
# include <windows.h>
#endif // _PreComp_
#endif
#endif //_PreComp_
#endif // MEASUREGUI_PRECOMPILED_H

View File

@@ -30,6 +30,8 @@
#include <Gui/WidgetFactory.h>
#include "DlgPrefsMeasureAppearanceImp.h"
#include "QuickMeasure.h"
#include "QuickMeasurePy.h"
#include "ViewProviderMeasureAngle.h"
#include "ViewProviderMeasureDistance.h"
#include "ViewProviderMeasureBase.h"
@@ -86,7 +88,7 @@ PyMOD_INIT_FUNC(MeasureGui)
CreateMeasureCommands();
MeasureGui::ViewProviderMeasureBase ::init();
MeasureGui::ViewProviderMeasure ::init();
MeasureGui::ViewProviderMeasure ::init();
MeasureGui::ViewProviderMeasureAngle ::init();
MeasureGui::ViewProviderMeasureDistance ::init();
@@ -95,5 +97,7 @@ PyMOD_INIT_FUNC(MeasureGui)
// Q_INIT_RESOURCE(Measure);
Base::Interpreter().addType(&MeasureGui::QuickMeasurePy::Type, mod, "QuickMeasure");
PyMOD_Return(mod);
}

View File

@@ -16,6 +16,7 @@ link_directories(${OCC_LIBRARY_DIR})
set(MeasureGui_LIBS
Measure
#Part
FreeCADGui
)
@@ -36,6 +37,8 @@ SET(MeasureGui_UIC_SRCS
DlgPrefsMeasureAppearanceImp.ui
)
generate_from_xml(QuickMeasurePy)
SET(MeasureGui_SRCS
${CMAKE_SOURCE_DIR}/src/Mod/Measure/InitGui.py
${MeasureGui_SRCS}
@@ -44,6 +47,10 @@ SET(MeasureGui_SRCS
Resources/Measure.qrc
PreCompiled.cpp
PreCompiled.h
QuickMeasurePy.xml
QuickMeasurePyImp.cpp
QuickMeasure.cpp
QuickMeasure.h
ViewProviderMeasureBase.cpp
ViewProviderMeasureBase.h
ViewProviderMeasureAngle.cpp

View File

@@ -0,0 +1,156 @@
/***************************************************************************
* Copyright (c) 2023 Pierre-Louis Boyer <development@Ondsel.com> *
* *
* 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"
#ifndef _PreComp_
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <cmath>
#include <vector>
#endif
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/Link.h>
#include <App/Part.h>
#include <Base/UnitsApi.h>
#include <Gui/Application.h>
#include <Gui/MainWindow.h>
#include <Gui/Selection.h>
#include <Mod/Part/App/PartFeature.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/Measure/App/Measurement.h>
#include "QuickMeasure.h"
using namespace Measure;
using namespace MeasureGui;
QuickMeasure::QuickMeasure(QObject* parent) : QObject(parent)
{
measurement = new Measure::Measurement();
}
QuickMeasure::~QuickMeasure()
{
delete measurement;
}
void QuickMeasure::onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (msg.Type == Gui::SelectionChanges::SetPreselect || msg.Type == Gui::SelectionChanges::RmvPreselect) {
return;
}
Gui::Document* doc = Gui::Application::Instance->activeDocument();
if (!doc) { return; }
measurement->clear();
std::vector<Part::TopoShape> subShapes;
std::vector<App::DocumentObject*> docsToMove;
for (auto& selObj : Gui::Selection().getSelectionEx()) {
App::DocumentObject* obj = selObj.getObject();
const std::vector<std::string> subNames = selObj.getSubNames();
if (subNames.empty()) {
measurement->addReference3D(obj, "");
}
else {
for (auto& subName : subNames) {
measurement->addReference3D(obj, subName);
}
}
}
MeasureType mtype = measurement->getType();
if (mtype == MeasureType::Volumes) {
Base::Quantity area(measurement->area(), Base::Unit::Area);
Base::Quantity vol(measurement->volume(), Base::Unit::Volume);
print(tr("Volume: %1, Area: %2").arg(vol.getSafeUserString()).arg(area.getSafeUserString()));
}
else if (mtype == MeasureType::Surfaces) {
Base::Quantity area(measurement->area(), Base::Unit::Area);
print(tr("Total area: %1").arg(area.getUserString()));
}
else if (mtype == MeasureType::TwoPlanes) {
Base::Quantity dist(measurement->planePlaneDistance(), Base::Unit::Length);
print(tr("Nominal distance: %1").arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::Cone || mtype == MeasureType::Plane) {
Base::Quantity area(measurement->area(), Base::Unit::Area);
print(tr("Area: %1").arg(area.getUserString()));
}
else if (mtype == MeasureType::Cylinder || mtype == MeasureType::Sphere || mtype == MeasureType::Torus) {
Base::Quantity area(measurement->area(), Base::Unit::Area);
Base::Quantity rad(measurement->radius(), Base::Unit::Length);
print(tr("Area: %1, Radius: %2").arg(area.getSafeUserString()).arg(rad.getSafeUserString()));
}
else if (mtype == MeasureType::Edges) {
Base::Quantity dist(measurement->length(), Base::Unit::Length);
print(tr("Total length: %1").arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::TwoParallelLines) {
Base::Quantity dist(measurement->lineLineDistance(), Base::Unit::Length);
print(tr("Nominal distance: %1").arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::TwoLines) {
Base::Quantity angle(measurement->angle(), Base::Unit::Length);
Base::Quantity dist(measurement->length(), Base::Unit::Length);
print(tr("Angle: %1, Total length: %2").arg(angle.getSafeUserString()).arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::Line) {
Base::Quantity dist(measurement->length(), Base::Unit::Length);
print(tr("Length: %1").arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::Circle) {
Base::Quantity dist(measurement->radius(), Base::Unit::Length);
print(tr("Radius: %1").arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::PointToPoint) {
Base::Quantity dist(measurement->length(), Base::Unit::Length);
print(tr("Distance: %1").arg(dist.getSafeUserString()));
}
else if (mtype == MeasureType::PointToEdge || mtype == MeasureType::PointToSurface) {
Base::Quantity dist(measurement->length(), Base::Unit::Length);
print(tr("Minimum distance: %1").arg(dist.getSafeUserString()));
}
else {
print(QString::fromLatin1(""));
}
}
void QuickMeasure::print(const QString& message)
{
Gui::getMainWindow()->setRightSideMessage(message);
}
#include "moc_QuickMeasure.cpp"

View File

@@ -0,0 +1,57 @@
/***************************************************************************
* Copyright (c) 2023 Pierre-Louis Boyer <development@Ondsel.com> *
* *
* 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 *
* *
***************************************************************************/
#ifndef MEASUREGUI_QUICKMEASURE_H
#define MEASUREGUI_QUICKMEASURE_H
#include <QObject>
#include <Mod/Measure/MeasureGlobal.h>
#include <Gui/Selection.h>
namespace Measure {
class Measurement;
}
namespace MeasureGui {
class QuickMeasure : public QObject, Gui::SelectionObserver
{
Q_OBJECT
public:
explicit QuickMeasure(QObject* parent = nullptr);
~QuickMeasure() override;
private:
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
void print(const QString& message);
Measure::Measurement* measurement;
};
} //namespace MeasureGui
#endif // MEASUREGUI_QUICKMEASURE_H

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="PyObjectBase"
Name="QuickMeasurePy"
Twin="QuickMeasure"
TwinPointer="QuickMeasure"
Include="Mod/Measure/Gui/QuickMeasure.h"
Namespace="MeasureGui"
FatherInclude="Base/PyObjectBase.h"
Constructor="true"
FatherNamespace="Base">
<Documentation>
<Author Licence="LGPL" Name="Ondsel" EMail="development@ondsel.com" />
<UserDocu>Selection Observer for the QuickMeasure label.</UserDocu>
</Documentation>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,64 @@
/***************************************************************************
* Copyright (c) 2023 Pierre-Louis Boyer <development@Ondsel.com> *
* *
* 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"
#ifndef _PreComp_
#endif
#include <App/Application.h>
#include <App/Document.h>
#include <Base/GeometryPyCXX.h>
// inclusion of the generated files (generated out of QuickMeasurePy.xml)
#include "QuickMeasurePy.h"
#include "QuickMeasurePy.cpp"
using namespace MeasureGui;
// returns a string which represents the object e.g. when printed in python
std::string QuickMeasurePy::representation() const
{
return "<MeasureGui::QuickMeasure>";
}
PyObject *QuickMeasurePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
{
// create a new instance of BoundBoxPy and the Twin object
return new QuickMeasurePy(new QuickMeasure);
}
// constructor method
int QuickMeasurePy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
PyObject *QuickMeasurePy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int QuickMeasurePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -41,3 +41,5 @@ FreeCAD.MeasureManager.addMeasureType(
MeasureCOM,
)
import MeasureGui
MeasureGui.QuickMeasure()