FreeCAD / src /Mod /Measure /Gui /ViewProviderMeasureAngle.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* Copyright (c) 2013 Thomas Anderson <blobfish[at]gmx.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 <sstream>
#include <QApplication>
#include <Inventor/draggers/SoTranslate2Dragger.h>
#include <Inventor/nodes/SoAnnotation.h>
#include <Inventor/nodes/SoBaseColor.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoFontStyle.h>
#include <Inventor/nodes/SoIndexedLineSet.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Inventor/nodes/SoText2.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/engines/SoCalculator.h>
#include <Inventor/engines/SoComposeVec3f.h>
#include <Inventor/engines/SoConcatenate.h>
#include <Inventor/engines/SoComposeMatrix.h>
#include <Inventor/engines/SoComposeRotation.h>
#include <Inventor/engines/SoComposeRotationFromTo.h>
#include <Inventor/engines/SoDecomposeRotation.h>
#include <Inventor/engines/SoTransformVec3f.h>
#include <Inventor/nodekits/SoShapeKit.h>
#include <Inventor/nodes/SoFont.h>
#include <Inventor/nodes/SoLineSet.h>
#include <Inventor/nodes/SoMatrixTransform.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoTransform.h>
#include <Inventor/nodes/SoVertexProperty.h>
#include <Inventor/nodekits/SoBaseKit.h>
#include <Precision.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Line.hxx>
#include <gp_Vec.hxx>
#include <gp_Lin.hxx>
#include <gp_Pnt.hxx>
#include <GeomAPI_ExtremaCurveCurve.hxx>
#include <GeomAPI_ProjectPointOnCurve.hxx>
#include <App/Application.h>
#include <App/Document.h>
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Quantity.h>
#include <Base/Tools.h>
#include <Gui/ArcEngine.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/ViewParams.h>
#include <Mod/Measure/App/Preferences.h>
#include "ViewProviderMeasureAngle.h"
using namespace MeasureGui;
using namespace Measure;
gp_Lin getLine(gp_Vec& vec, gp_Vec& origin)
{
gp_Pnt tempOrigin;
tempOrigin.SetXYZ(origin.XYZ());
return gp_Lin(tempOrigin, gp_Dir(vec));
}
SbMatrix ViewProviderMeasureAngle::getMatrix()
{
// Code ported from src/Mod/Part/Gui/TaskDimension.cpp
if (pcObject == nullptr) {
throw Base::RuntimeError("no DocumentObject");
}
Measure::MeasureAngle* measurement = static_cast<Measure::MeasureAngle*>(pcObject);
if (!measurement->Element1.getValue() || measurement->Element1.getSubValues().empty()) {
return SbMatrix();
}
if (!measurement->Element2.getValue() || measurement->Element2.getSubValues().empty()) {
return SbMatrix();
}
gp_Vec vector1 = measurement->vector1();
gp_Vec vector2 = measurement->vector2();
gp_Vec loc1 = measurement->location1();
gp_Vec loc2 = measurement->location2();
if (vector1.Magnitude() == 0 || vector2.Magnitude() == 0) {
return SbMatrix();
}
gp_Lin lin1 = getLine(vector1, loc1);
gp_Lin lin2 = getLine(vector2, loc2);
SbMatrix dimSys = SbMatrix();
double radius;
if (vector1.IsParallel(vector2, Precision::Angular())) {
// take first point project it onto second vector.
Handle(Geom_Curve) heapLine2 = new Geom_Line(lin2);
gp_Pnt tempPoint(loc1.XYZ());
GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine2);
if (projection.NbPoints() < 1) {
throw Base::RuntimeError("parallel vectors: could not project onto line");
}
gp_Vec newPoint2;
newPoint2.SetXYZ(projection.Point(1).XYZ());
// if points are colinear, projection doesn't work and returns the same point.
// In this case we just use the original point.
if ((newPoint2 - loc1).Magnitude() < Precision::Confusion()) {
newPoint2 = loc2;
}
// now get midpoint between for dim origin.
gp_Vec point1 = loc1;
gp_Vec midPointProjection = newPoint2 - point1;
double distance = midPointProjection.Magnitude();
midPointProjection.Normalize();
midPointProjection *= distance / 2.0;
gp_Vec origin = point1 + midPointProjection;
// yaxis should be the same as vector1, but doing this to eliminate any potential slop from
// using precision::angular. If lines are colinear and we have no plane, we can't establish
// zAxis from crossing. we just the absolute axis.
gp_Vec xAxis = (point1 - origin).Normalized();
gp_Vec zAxis;
if (xAxis.IsParallel(vector1, Precision::Angular())) {
if (!xAxis.IsParallel(gp_Vec(0.0, 0.0, 1.0), Precision::Angular())) {
zAxis = gp_Vec(0.0, 0.0, 1.0);
}
else {
zAxis = gp_Vec(0.0, 1.0, 0.0);
}
}
else {
zAxis = xAxis.Crossed(vector1).Normalized();
}
gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized();
zAxis = xAxis.Crossed(yAxis).Normalized();
dimSys = SbMatrix(
xAxis.X(),
yAxis.X(),
zAxis.X(),
origin.X(),
xAxis.Y(),
yAxis.Y(),
zAxis.Y(),
origin.Y(),
xAxis.Z(),
yAxis.Z(),
zAxis.Z(),
origin.Z(),
0.0,
0.0,
0.0,
1.0
);
dimSys = dimSys.transpose();
radius = midPointProjection.Magnitude();
}
else {
Handle(Geom_Curve) heapLine1 = new Geom_Line(lin1);
Handle(Geom_Curve) heapLine2 = new Geom_Line(lin2);
GeomAPI_ExtremaCurveCurve extrema(heapLine1, heapLine2);
if (extrema.NbExtrema() < 1) {
throw Base::RuntimeError("Could not get extrema");
}
gp_Pnt extremaPoint1, extremaPoint2, dimensionOriginPoint;
extrema.Points(1, extremaPoint1, extremaPoint2);
if (extremaPoint1.Distance(extremaPoint2) < Precision::Confusion()) {
dimensionOriginPoint = extremaPoint1;
}
else {
// find halfway point in between extrema points for dimension origin.
gp_Vec vec1(extremaPoint1.XYZ());
gp_Vec vec2(extremaPoint2.XYZ());
gp_Vec connection(vec2 - vec1);
Standard_Real distance = connection.Magnitude();
connection.Normalize();
connection *= (distance / 2.0);
dimensionOriginPoint.SetXYZ((vec1 + connection).XYZ());
}
gp_Vec thirdPoint(loc2);
gp_Vec originVector(dimensionOriginPoint.XYZ());
gp_Vec extrema2Vector(extremaPoint2.XYZ());
radius = (loc1 - originVector).Magnitude();
double legOne = (extrema2Vector - originVector).Magnitude();
if (legOne > Precision::Confusion()) {
double legTwo = sqrt(pow(radius, 2) - pow(legOne, 2));
gp_Vec projectionVector(vector2);
projectionVector.Normalize();
projectionVector *= legTwo;
thirdPoint = extrema2Vector + projectionVector;
gp_Vec hyp(thirdPoint - originVector);
hyp.Normalize();
gp_Vec otherSide(loc1 - originVector);
otherSide.Normalize();
}
gp_Vec xAxis = (loc1 - originVector).Normalized();
gp_Vec fakeYAxis = (thirdPoint - originVector).Normalized();
gp_Vec zAxis = (xAxis.Crossed(fakeYAxis)).Normalized();
gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized();
dimSys = SbMatrix(
xAxis.X(),
yAxis.X(),
zAxis.X(),
dimensionOriginPoint.X(),
xAxis.Y(),
yAxis.Y(),
zAxis.Y(),
dimensionOriginPoint.Y(),
xAxis.Z(),
yAxis.Z(),
zAxis.Z(),
dimensionOriginPoint.Z(),
0.0,
0.0,
0.0,
1.0
);
dimSys = dimSys.transpose();
}
return dimSys;
}
PROPERTY_SOURCE(MeasureGui::ViewProviderMeasureAngle, MeasureGui::ViewProviderMeasureBase)
ViewProviderMeasureAngle::ViewProviderMeasureAngle()
{
sPixmap = "Measurement-Angle";
// Primary Arc
Gui::ArcEngine* arcEngine = new Gui::ArcEngine();
arcEngine->angle.connectFrom(&fieldAngle);
auto calculatorRadius = new SoCalculator();
calculatorRadius->A.connectFrom(&pDragger->translation);
calculatorRadius->expression.setValue("oa=length(A)");
arcEngine->radius.connectFrom(&calculatorRadius->oa);
arcEngine->deviation.setValue(0.1f);
SoCoordinate3* coordinates = new SoCoordinate3();
coordinates->point.connectFrom(&arcEngine->points);
SoLineSet* lineSet = new SoLineSet();
lineSet->vertexProperty.setValue(coordinates);
lineSet->numVertices.connectFrom(&arcEngine->pointCount);
lineSet->startIndex.setValue(0);
pLineSeparator->addChild(lineSet);
// Secondary Arc, from midpoint to label
auto engineAngle = new SoCalculator();
engineAngle->A.connectFrom(&arcEngine->midpoint);
engineAngle->B.connectFrom(&pLabelTranslation->translation);
engineAngle->expression.setValue(
"tA=normalize(A); tB=normalize(B); oa=atan2(tB[1], tB[0])-atan2(tA[1], tA[0])"
);
Gui::ArcEngine* arcEngineSecondary = new Gui::ArcEngine();
arcEngineSecondary->radius.connectFrom(&calculatorRadius->oa);
arcEngineSecondary->deviation.setValue(0.1f);
arcEngineSecondary->angle.connectFrom(&engineAngle->oa);
// Rotate arc
auto engineRotMidpoint = new SoComposeRotationFromTo(); // absolute angle to midpoint
engineRotMidpoint->from.setValue(SbVec3f(1.0, 0.0, 0.0));
engineRotMidpoint->to.connectFrom(&arcEngine->midpoint);
auto matrixEngine = new SoComposeMatrix();
matrixEngine->rotation.connectFrom(&engineRotMidpoint->rotation);
auto transformEngine = new SoTransformVec3f();
transformEngine->matrix.connectFrom(&matrixEngine->matrix);
transformEngine->vector.connectFrom(&arcEngineSecondary->points);
SoCoordinate3* coordinatesSecondary = new SoCoordinate3();
coordinatesSecondary->point.connectFrom(&transformEngine->point);
SoLineSet* lineSetSecondary = new SoLineSet();
lineSetSecondary->vertexProperty.setValue(coordinatesSecondary);
lineSetSecondary->numVertices.connectFrom(&arcEngineSecondary->pointCount);
lineSetSecondary->startIndex.setValue(0);
pLineSeparatorSecondary->addChild(lineSetSecondary);
}
void ViewProviderMeasureAngle::redrawAnnotation()
{
auto obj = dynamic_cast<Measure::MeasureAngle*>(getMeasureObject());
double angleDeg = obj->Angle.getValue();
this->fieldAngle = Base::toRadians(angleDeg);
// Set matrix
try {
SbMatrix matrix = getMatrix();
pcTransform->setMatrix(matrix);
}
catch (const Base::Exception& e) {
Base::Console().error("Error in ViewProviderMeasureAngle::redrawAnnotation: %s\n", e.what());
return;
}
// Set Label
setLabelValue(static_cast<Measure::MeasureBase*>(pcObject)->getResultString());
}
//! return the feature as a MeasureAngle
Measure::MeasureAngle* ViewProviderMeasureAngle::getMeasureAngle()
{
Measure::MeasureAngle* feature = dynamic_cast<Measure::MeasureAngle*>(pcObject);
if (!feature) {
throw Base::RuntimeError("Feature not found for ViewProviderMeasureAngle");
}
return feature;
}
void ViewProviderMeasureAngle::positionAnno(const Measure::MeasureBase* measureObject)
{
(void)measureObject;
setLabelTranslation(SbVec3f(0, 0.1 * getViewScale(), 0));
}