Merge pull request #9750 from hlorus/gsoc2023_measure

GSoC 2023: Unified Measurement Facility
This commit is contained in:
WandererFan
2024-04-29 12:41:54 -04:00
committed by GitHub
99 changed files with 7895 additions and 4542 deletions

View File

@@ -130,6 +130,7 @@ macro(InitializeFreeCADBuildOptions)
option(BUILD_SPREADSHEET "Build the FreeCAD spreadsheet module" ON)
option(BUILD_START "Build the FreeCAD start module" ON)
option(BUILD_TEST "Build the FreeCAD test module" ON)
option(BUILD_MEASURE "Build the FreeCAD Measure module" ON)
option(BUILD_TECHDRAW "Build the FreeCAD Technical Drawing module" ON)
option(BUILD_TUX "Build the FreeCAD Tux module" ON)
option(BUILD_WEB "Build the FreeCAD Web module" ON)

View File

@@ -118,6 +118,7 @@ macro(PrintFinalReport)
value(BUILD_SPREADSHEET)
value(BUILD_START)
value(BUILD_SURFACE)
value(BUILD_MEASURE)
value(BUILD_TECHDRAW)
value(BUILD_TEST)
value(BUILD_TUX)

View File

@@ -103,6 +103,7 @@
#include "VarSet.h"
#include "MaterialObject.h"
#include "MeasureDistance.h"
#include "MeasureManagerPy.h"
#include "Origin.h"
#include "OriginFeature.h"
#include "OriginGroupExtension.h"
@@ -315,6 +316,8 @@ void Application::setupPythonTypes()
Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material");
Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata");
Base::Interpreter().addType(&App::MeasureManagerPy::Type, pAppModule, "MeasureManager");
Base::Interpreter().addType(&App::StringHasherPy::Type, pAppModule, "StringHasher");
Base::Interpreter().addType(&App::StringIDPy::Type, pAppModule, "StringID");

View File

@@ -97,6 +97,7 @@ generate_from_xml(StringIDPy)
generate_from_xml(ComplexGeoDataPy)
generate_from_xml(PropertyContainerPy)
generate_from_xml(MaterialPy)
generate_from_xml(MeasureManagerPy)
generate_from_py(FreeCADInit InitScript.h)
generate_from_py(FreeCADTest TestScript.h)
@@ -119,6 +120,7 @@ SET(FreeCADApp_XML_SRCS
PropertyContainerPy.xml
ComplexGeoDataPy.xml
MaterialPy.xml
MeasureManagerPy.xml
StringHasherPy.xml
StringIDPy.xml
)
@@ -283,6 +285,8 @@ SET(FreeCADApp_CPP_SRCS
MappedName.cpp
Material.cpp
MaterialPyImp.cpp
MeasureManager.cpp
MeasureManagerPyImp.cpp
Metadata.cpp
MetadataPyImp.cpp
ElementNamingUtils.cpp
@@ -307,6 +311,7 @@ SET(FreeCADApp_HPP_SRCS
MappedName.h
MappedElement.h
Material.h
MeasureManager.h
Metadata.h
ElementNamingUtils.h
StringHasher.h

180
src/App/MeasureManager.cpp Normal file
View File

@@ -0,0 +1,180 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* Copyright (c) 2023 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 <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/VectorPy.h>
#include <App/Document.h>
#include "MeasureManager.h"
namespace App {
std::vector<MeasureHandler> MeasureManager::_mMeasureHandlers;
std::vector<MeasureType*> MeasureManager::_mMeasureTypes;
MeasureManager::MeasureManager()
{
// Constructor implementation
}
void MeasureManager::addMeasureHandler(const char* module, MeasureTypeMethod typeCb) {
auto item = new MeasureHandler{module, typeCb};
_mMeasureHandlers.push_back(*item);
}
bool MeasureManager::hasMeasureHandler(const char* module) {
for(MeasureHandler& handler : _mMeasureHandlers) {
if (strcmp(handler.module.c_str(), module) == 0) {
return true;
}
}
return false;
}
MeasureHandler MeasureManager::getMeasureHandler(const char* module) {
for(MeasureHandler handler : _mMeasureHandlers) {
if (!strcmp(handler.module.c_str(), module)) {
return handler;
}
}
MeasureHandler empty;
return empty;
}
void MeasureManager::addMeasureType(MeasureType* measureType) {
_mMeasureTypes.push_back(measureType);
}
void MeasureManager::addMeasureType(std::string id, std::string label, std::string measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb) {
MeasureType* mType = new MeasureType{id, label, measureObj, validatorCb, prioritizeCb, false, nullptr};
_mMeasureTypes.push_back(mType);
}
void MeasureManager::addMeasureType(const char* id, const char* label, const char* measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb) {
addMeasureType(std::string(id), std::string(label), std::string(measureObj), validatorCb, prioritizeCb);
}
const std::vector<MeasureType*> MeasureManager::getMeasureTypes() {
return _mMeasureTypes;
}
Py::Tuple MeasureManager::getSelectionPy(const App::MeasureSelection& selection) {
// Convert selection to python list
Py::Tuple selectionPy(selection.size());
int i = 0;
for (auto it : selection) {
Py::Dict sel;
sel.setItem("object", Py::asObject(it.object.getObject()->getPyObject()));
sel.setItem("subName", Py::String(it.object.getSubName()));
sel.setItem("pickedPoint", Py::asObject(new Base::VectorPy(it.pickedPoint)));
selectionPy.setItem(i, sel);
i++;
}
return selectionPy;
}
std::vector<MeasureType*> MeasureManager::getValidMeasureTypes(App::MeasureSelection selection, std::string mode) {
Base::PyGILStateLocker lock;
// Convert selection to python list
Py::Tuple selectionPy = getSelectionPy(selection);
// Store valid measure types
std::vector<MeasureType*> validTypes;
std::pair<int, MeasureType>();
// Loop through measure types and check if they work with given selection
for (App::MeasureType* mType : getMeasureTypes()){
if (mode != "" && mType->label != mode) {
continue;
}
if (mType->isPython) {
// Parse Python measure types
auto measurePyClass = Py::Object(mType->pythonClass);
Py::Tuple args(1);
args.setItem(0, selectionPy);
Py::Object isValid;
try {
isValid = measurePyClass.callMemberFunction(std::string("isValidSelection"), args);
} catch (const Py::Exception&) {
Base::PyException e;
e.ReportException();
isValid = Py::False();
}
if (isValid.as_bool()) {
// Check priority
Py::Object isPriority;
try {
isPriority = measurePyClass.callMemberFunction("isPrioritySelection", args);
} catch (const Py::Exception&) {
Base::PyException e;
e.ReportException();
isPriority = Py::False();
}
if (isPriority.as_bool()) {
validTypes.insert(validTypes.begin(), mType);
} else {
validTypes.push_back(mType);
}
}
} else {
// Parse c++ measure types
if (mType->validatorCb && !mType->validatorCb(selection)) {
continue;
}
// Check if the measurement type prioritizes the given selection
if (mType->prioritizeCb && mType->prioritizeCb(selection)) {
validTypes.insert(validTypes.begin(), mType);
} else {
validTypes.push_back(mType);
}
}
}
return validTypes;
}
} // namespace App

112
src/App/MeasureManager.h Normal file
View File

@@ -0,0 +1,112 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* Copyright (c) 2023 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 MEASUREMANAGER_H
#define MEASUREMANAGER_H
#include <vector>
#include <tuple>
#include <functional>
#include <string>
#include <Python.h>
#include <App/DocumentObject.h>
#include <Base/Vector3D.h>
#include <App/DocumentObserver.h>
#include <Base/Interpreter.h>
#include <FCGlobal.h>
namespace App {
// Add your class methods and member variables here
enum class MeasureElementType {
INVALID,
POINT,
LINE,
LINESEGMENT,
CIRCLE,
ARC,
CURVE, // Has a length but no radius or axis
PLANE,
CYLINDER,
Volume,
};
struct MeasureSelectionItem {
App::SubObjectT object;
Base::Vector3d pickedPoint;
};
using MeasureSelection = std::vector<MeasureSelectionItem>;
using MeasureValidateMethod = std::function<bool(const MeasureSelection&)>;
using MeasurePrioritizeMethod = std::function<bool(const MeasureSelection&)>;
using MeasureTypeMethod = std::function<MeasureElementType (App::DocumentObject*, const char*)>;
struct MeasureType {
std::string identifier;
std::string label;
std::string measureObject;
// Checks if the measurement works with a given selection
MeasureValidateMethod validatorCb;
// Allows to prioritize this over other measurement types when the measurement type is picked implicitly from the selection.
// Gets called only when validatorCb returned true for the given selection
MeasurePrioritizeMethod prioritizeCb;
bool isPython;
PyObject* pythonClass;
};
struct MeasureHandler {
std::string module;
MeasureTypeMethod typeCb;
};
class AppExport MeasureManager {
public:
MeasureManager();
static void addMeasureHandler(const char* module, MeasureTypeMethod typeCb);
static bool hasMeasureHandler(const char* module);
static MeasureHandler getMeasureHandler(const char* module);
static void addMeasureType(MeasureType* measureType);
static void addMeasureType(std::string id, std::string label, std::string measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb);
static void addMeasureType(const char* id, const char* label, const char* measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb);
static const std::vector<MeasureType*> getMeasureTypes();
static Py::Tuple getSelectionPy(const App::MeasureSelection& selection);
static std::vector<MeasureType*> getValidMeasureTypes(App::MeasureSelection selection, std::string mode);
private:
static std::vector<MeasureHandler> _mMeasureHandlers;
static std::vector<MeasureType*> _mMeasureTypes;
};
} // namespace App
#endif // MEASUREMANAGER_H

View File

@@ -0,0 +1,43 @@
<?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="MeasureManagerPy"
Twin="MeasureManager"
TwinPointer="MeasureManager"
Include="App/MeasureManager.h"
FatherInclude="Base/PyObjectBase.h"
Namespace="App"
Constructor="false"
Delete="true"
FatherNamespace="Base">
<Documentation>
<Author Licence="LGPL" Name="David Friedli" EMail="david@friedli-be.ch" />
<UserDocu>MeasureManager class.
The MeasureManager handles measure types and geometry handler accross FreeCAD.</UserDocu>
<DeveloperDocu>MeasureManager</DeveloperDocu>
</Documentation>
<Methode Name="addMeasureType" Static="true">
<Documentation>
<UserDocu>addMeasureType(id, label, measureType) -> None
Add a new measure type.
id : str
Unique identifier of the measure type.
label : str
Name of the module.
measureType : Measure.MeasureBasePython
The actual measure type.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getMeasureTypes" Static="true">
<Documentation>
<UserDocu>getMeasureTypes() -> List[(id, label, pythonMeasureType)]
Returns a list of all registered measure types.</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,79 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* Copyright (c) 2023 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"
// inclusion of the generated files (generated out of MeasureManagerPy.xml)
#include "MeasureManagerPy.h"
#include "MeasureManagerPy.cpp"
using namespace App;
// returns a string which represents the object e.g. when printed in python
std::string MeasureManagerPy::representation() const
{
return "<App::MeasureManager>";
}
PyObject* MeasureManagerPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int MeasureManagerPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}
PyObject* MeasureManagerPy::addMeasureType(PyObject *args)
{
PyObject *pyobj = Py_None;
char *id, *label;
if (!PyArg_ParseTuple(args, "ssO", &id, &label, &pyobj))
return nullptr;
MeasureManager::addMeasureType(
new App::MeasureType{id, label, "", nullptr, nullptr, true, pyobj}
);
Py_Return;
}
PyObject* MeasureManagerPy::getMeasureTypes(PyObject *args)
{
Py::List types;
for (auto & it : MeasureManager::getMeasureTypes()) {
Py::Tuple type(3);
type.setItem(0, Py::String(it->identifier));
type.setItem(1, Py::String(it->label));
type.setItem(2, Py::Object(it->pythonClass));
types.append(type);
}
return Py::new_reference_to(types);
}

View File

@@ -128,7 +128,6 @@ public:
static void init();
static void destruct();
protected:
static std::string getModuleName(const char* ClassName);

135
src/Gui/ArcEngine.cpp Normal file
View File

@@ -0,0 +1,135 @@
/***************************************************************************
* Copyright (c) 2013 Thomas Anderson <blobfish[at]gmx.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"
#include "ArcEngine.h"
#include <vector>
# include <Inventor/engines/SoCalculator.h>
# include <Inventor/engines/SoComposeVec3f.h>
# include <Inventor/engines/SoConcatenate.h>
# include <Inventor/engines/SoComposeRotation.h>
# include <Inventor/engines/SoComposeRotationFromTo.h>
# include <Inventor/nodekits/SoShapeKit.h>
# include <Inventor/nodes/SoCone.h>
# include <Inventor/nodes/SoFont.h>
# include <Inventor/nodes/SoLineSet.h>
# include <Inventor/nodes/SoMaterial.h>
# include <Inventor/nodes/SoMatrixTransform.h>
# include <Inventor/nodes/SoResetTransform.h>
# include <Inventor/nodes/SoSeparator.h>
# include <Inventor/nodes/SoTransform.h>
# include <Inventor/nodes/SoVertexProperty.h>
using namespace Gui;
SO_ENGINE_SOURCE(ArcEngine)
ArcEngine::ArcEngine()
{
SO_ENGINE_CONSTRUCTOR(ArcEngine);
SO_ENGINE_ADD_INPUT(radius, (10.0));
SO_ENGINE_ADD_INPUT(angle, (1.0));
SO_ENGINE_ADD_INPUT(deviation, (0.25));
SO_ENGINE_ADD_OUTPUT(points, SoMFVec3f);
SO_ENGINE_ADD_OUTPUT(pointCount, SoSFInt32);
SO_ENGINE_ADD_OUTPUT(midpoint, SoSFVec3f);
}
void ArcEngine::initClass()
{
SO_ENGINE_INIT_CLASS(ArcEngine, SoEngine, "Engine");
}
void ArcEngine::evaluate()
{
float angle = abs(this->angle.getValue());
if (radius.getValue() < std::numeric_limits<float>::epsilon() ||
deviation.getValue() < std::numeric_limits<float>::epsilon())
{
defaultValues();
return;
}
float deviationAngle(acos((radius.getValue() - deviation.getValue()) / radius.getValue()));
std::vector<SbVec3f> tempPoints;
int segmentCount;
if (deviationAngle >= angle) {
segmentCount = 1;
}
else {
segmentCount = static_cast<int>(angle / deviationAngle) + 1;
if (segmentCount < 2) {
defaultValues();
return;
}
}
float angleIncrement = (this->angle.getValue() > 0 ? angle : -angle) / static_cast<float>(segmentCount);
for (int index = 0; index < segmentCount + 1; ++index)
{
SbVec3f currentNormal(1.0, 0.0, 0.0);
float currentAngle = index * angleIncrement;
SbRotation rotation(SbVec3f(0.0, 0.0, 1.0), currentAngle);
rotation.multVec(currentNormal, currentNormal);
tempPoints.push_back(currentNormal * radius.getValue());
}
int tempCount = tempPoints.size(); //for macro.
SO_ENGINE_OUTPUT(points, SoMFVec3f, setNum(tempCount));
SO_ENGINE_OUTPUT(pointCount, SoSFInt32, setValue(tempCount));
std::vector<SbVec3f>::const_iterator it;
for (it = tempPoints.begin(); it != tempPoints.end(); ++it)
{
int currentIndex = it-tempPoints.begin(); //for macro.
SbVec3f temp(*it); //for macro
SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(currentIndex, temp));
}
// Get Midpoint
float a = angle / 2;
SbRotation rot(SbVec3f(0.0, 0.0, 1.0), a);
SbVec3f midPnt(1.0, 0.0, 0.0);
rot.multVec(midPnt, midPnt);
midPnt = midPnt * radius.getValue();
SO_ENGINE_OUTPUT(midpoint, SoSFVec3f, setValue(midPnt));
}
void ArcEngine::defaultValues()
{
//just some non-failing info.
SO_ENGINE_OUTPUT(points, SoMFVec3f, setNum(2));
SbVec3f point1(10.0, 0.0, 0.0);
SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(0, point1));
SbVec3f point2(7.07f, 7.07f, 0.0);
SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(1, point2));
SO_ENGINE_OUTPUT(pointCount, SoSFInt32, setValue(2));
SbVec3f point3(7.07f, 7.07f, 0.0);
SO_ENGINE_OUTPUT(midpoint, SoSFVec3f, setValue(point3));
}

View File

@@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) 2022 *
* Copyright (c) 2013 Thomas Anderson <blobfish[at]gmx.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
@@ -20,39 +20,54 @@
* *
***************************************************************************/
#ifndef GUI_ARCENGINE_H
#define GUI_ARCENGINE_H
#ifndef PARTGUI_DIALOG_DLGSETTINGSMEASURE_H
#define PARTGUI_DIALOG_DLGSETTINGSMEASURE_H
#include <FCGlobal.h>
#include <Inventor/fields/SoMFColor.h>
#include <Inventor/engines/SoEngineOutput.h>
#include <Gui/PropertyPage.h>
#include <memory>
#include <Inventor/engines/SoSubEngine.h>
#include <Inventor/engines/SoEngine.h>
#include <Inventor/fields/SoSFColor.h>
#include <Inventor/fields/SoSFFloat.h>
#include <Inventor/fields/SoSFMatrix.h>
#include <Inventor/fields/SoSFRotation.h>
#include <Inventor/fields/SoSFString.h>
#include <Inventor/fields/SoSFVec3f.h>
#include <Inventor/nodekits/SoSeparatorKit.h>
namespace PartGui {
class Ui_DlgSettingsMeasure;
class SoText2;
class SoTranslation;
class SoCoordinate3;
class SoIndexedLineSet;
/**
* The DlgSettingsMeasure class implements a preference page to change color
* and font settings for Measure Dimensions
*/
class DlgSettingsMeasure : public Gui::Dialog::PreferencePage
namespace Gui {
// /*used for generating points for arc display*/
class GuiExport ArcEngine : public SoEngine
{
Q_OBJECT
SO_ENGINE_HEADER(ArcEngine);
public:
explicit DlgSettingsMeasure(QWidget* parent = nullptr);
~DlgSettingsMeasure() override;
ArcEngine();
static void initClass();
void saveSettings() override;
void loadSettings() override;
SoSFFloat radius;
SoSFFloat angle;
SoSFFloat deviation;
SoEngineOutput points;
SoEngineOutput pointCount;
SoEngineOutput midpoint;
protected:
void changeEvent(QEvent *e) override;
void evaluate() override;
private:
std::unique_ptr<Ui_DlgSettingsMeasure> ui;
void onMeasureRefresh();
~ArcEngine() override{}
void defaultValues(); //some non error values if something goes wrong.
};
} // namespace PartGui
} // namespace Gui
#endif // PARTGUI_DIALOG_DLGSETTINGSMEASURE_H
#endif // GUI_ARCENGINE_H

View File

@@ -455,6 +455,7 @@ SET(Dialog_CPP_SRCS
DownloadManager.cpp
DocumentRecovery.cpp
TaskElementColors.cpp
TaskMeasure.cpp
DlgObjectSelection.cpp
DlgAddProperty.cpp
VectorListEditor.cpp
@@ -494,6 +495,7 @@ SET(Dialog_HPP_SRCS
DownloadManager.h
DocumentRecovery.h
TaskElementColors.h
TaskMeasure.h
DlgObjectSelection.h
DlgAddProperty.h
VectorListEditor.h
@@ -1009,6 +1011,7 @@ SET(Inventor_CPP_SRCS
SoTouchEvents.cpp
SoMouseWheelEvent.cpp
SoFCCSysDragger.cpp
ArcEngine.cpp
)
SET(Inventor_SRCS
${Inventor_CPP_SRCS}
@@ -1037,6 +1040,7 @@ SET(Inventor_SRCS
SoTouchEvents.h
SoMouseWheelEvent.h
SoFCCSysDragger.h
ArcEngine.h
)
SOURCE_GROUP("View3D\\Inventor" FILES ${Inventor_SRCS})

View File

@@ -76,6 +76,7 @@
#include "SelectionObject.h"
#include "SoAxisCrossKit.h"
#include "SoFCOffscreenRenderer.h"
#include "TaskMeasure.h"
#include "TextureMapping.h"
#include "Tools.h"
#include "Tree.h"
@@ -3189,6 +3190,38 @@ bool StdCmdMeasureDistance::isActive()
return false;
}
//===========================================================================
// Std_Measure
// this is the Unified Measurement Facility Measure command
//===========================================================================
DEF_STD_CMD_A(StdCmdMeasure)
StdCmdMeasure::StdCmdMeasure()
:Command("Std_Measure")
{
sGroup = "Measure";
sMenuText = QT_TR_NOOP("&Measure");
sToolTipText = QT_TR_NOOP("Measure a feature");
sWhatsThis = "Std_Measure";
sStatusTip = QT_TR_NOOP("Measure a feature");
sPixmap = "umf-measurement";
}
void StdCmdMeasure::activated(int iMsg)
{
Q_UNUSED(iMsg);
TaskMeasure *task = new TaskMeasure();
Gui::Control().showDialog(task);
}
bool StdCmdMeasure::isActive(){
return true;
}
//===========================================================================
// Std_SceneInspector
//===========================================================================
@@ -4117,6 +4150,7 @@ void CreateViewStdCommands()
rcCmdMgr.addCommand(new StdCmdTreeCollapse());
rcCmdMgr.addCommand(new StdCmdTreeSelectAllInstances());
rcCmdMgr.addCommand(new StdCmdMeasureDistance());
rcCmdMgr.addCommand(new StdCmdMeasure());
rcCmdMgr.addCommand(new StdCmdSceneInspector());
rcCmdMgr.addCommand(new StdCmdTextureMapping());
rcCmdMgr.addCommand(new StdCmdDemoMode());

View File

@@ -135,6 +135,7 @@
<file>view-rotate-right.svg</file>
<file>view-measurement.svg</file>
<file>view-measurement-cross.svg</file>
<file>umf-measurement.svg</file>
<file>Tree_Annotation.svg</file>
<file>Tree_Dimension.svg</file>
<file>Tree_Python.svg</file>

View File

@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
id="svg2869"
version="1.1"
viewBox="0 0 64 64"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2871">
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop10" />
<stop
style="stop-color:#888a85;stop-opacity:1"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop19" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop20" />
</linearGradient>
<linearGradient
id="swatch18">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop18" />
</linearGradient>
<linearGradient
id="swatch15">
<stop
style="stop-color:#3d0000;stop-opacity:1;"
offset="0"
id="stop15" />
</linearGradient>
<linearGradient
id="linearGradient5-1">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
id="linearGradient3836-9">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11"
x1="12.375"
y1="23.75"
x2="64"
y2="28.5"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata2874">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[maxwxyz]</dc:title>
</cc:Agent>
</dc:creator>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:date>2024</dc:date>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer3"
style="display:inline">
<path
id="path4498"
d="M 31.906888,60.999998 V 12.276532 l -24.5860006,1e-6 -2.904e-4,-3.0939183 24.58542,-6.1826135 h 8.71e-4 15.462656 v 9.2765308 h 13.308118 l 2.89e-4,2.030582 -13.308407,4.061163 v 42.631722 z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path6"
d="m 32.154297,5 h 13.214844 v 9.277344 h 8.55664 l -8.55664,2.611328 V 59 H 33.90625 V 10.277344 H 11.164063 Z"
style="fill:url(#linearGradient11);fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4504"
d="m 33.453879,17.976538 9.277594,8.71e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1"
d="m 33.457361,48.841436 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path2"
d="m 33.457361,41.125284 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3"
d="m 33.457361,33.409133 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4"
d="m 33.457361,25.692981 6.185063,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5"
d="m 33.453879,56.557588 9.277594,8.72e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path11"
d="m 34.230469,19.976538 7.724414,8.71e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path12"
d="m 34.233368,50.841436 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path13"
d="m 34.233368,43.125284 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path14"
d="m 34.233368,35.409133 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path15"
d="m 34.233368,27.692981 5.14961,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path16"
d="m 34.230469,58.557588 8.501004,8.72e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4500"
d="M 7.3208874,27.158429 H 34.22781 l 0.0019,28.741042 H 28.043227 V 37.209528 L 7.3208874,32.571857 Z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4502"
d="m 45.82444,20.585383 14.854673,4.062178 2.9e-4,2.510868 H 50.463816 l 0.0016,14.741042 h -4.638797 z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path7"
d="M 9.3203125,29.158203 H 32.228516 l 0.002,24.742188 h -2.1875 V 35.607422 L 9.3203125,30.970703 Z"
style="fill:#888a85;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path18"
d="m 31.176195,46.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path19"
d="m 31.176195,48.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path20"
d="m 31.176195,42.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path21"
d="m 31.176195,44.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path22"
d="m 31.176195,38.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path23"
d="m 31.176195,40.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path24"
d="m 31.176195,50.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path25"
d="m 31.176195,52.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path26"
d="m 31.176195,34.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path27"
d="m 31.176195,36.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -74,6 +74,7 @@
#include "Inventor/SoAutoZoomTranslation.h"
#include "Inventor/SoDrawingGrid.h"
#include "propertyeditor/PropertyItem.h"
#include "ArcEngine.h"
using namespace Gui;
@@ -193,6 +194,8 @@ void Gui::SoFCDB::init()
SelectionObject ::init();
ArcEngine ::initClass();
qRegisterMetaType<Base::Vector3f>("Base::Vector3f");
qRegisterMetaType<Base::Vector3d>("Base::Vector3d");
qRegisterMetaType<Base::Quantity>("Base::Quantity");

388
src/Gui/TaskMeasure.cpp Normal file
View File

@@ -0,0 +1,388 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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_
# include <QApplication>
# include <QKeyEvent>
#endif
#include "TaskMeasure.h"
#include "Control.h"
#include "MainWindow.h"
#include "Application.h"
#include "App/Document.h"
#include "App/DocumentObjectGroup.h"
#include <Gui/BitmapFactory.h>
#include <QFormLayout>
#include <QPushButton>
using namespace Gui;
TaskMeasure::TaskMeasure()
{
qApp->installEventFilter(this);
this->setButtonPosition(TaskMeasure::South);
auto taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("umf-measurement"), tr("Measurement"), true, nullptr);
// Create mode dropdown and add all registered measuretypes
modeSwitch = new QComboBox();
modeSwitch->addItem(QString::fromLatin1("Auto"));
for (App::MeasureType* mType : App::MeasureManager::getMeasureTypes()){
modeSwitch->addItem(QString::fromLatin1(mType->label.c_str()));
}
// Connect dropdown's change signal to our onModeChange slot
connect(modeSwitch, qOverload<int>(&QComboBox::currentIndexChanged), this, &TaskMeasure::onModeChanged);
// Result widget
valueResult = new QLineEdit();
valueResult->setReadOnly(true);
// Main layout
QBoxLayout *layout = taskbox->groupLayout();
QFormLayout* formLayout = new QFormLayout();
formLayout->setHorizontalSpacing(10);
// Note: How can the split between columns be kept in the middle?
// formLayout->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy::ExpandingFieldsGrow);
formLayout->setFormAlignment(Qt::AlignCenter);
formLayout->addRow(QString::fromLatin1("Mode:"), modeSwitch);
formLayout->addRow(QString::fromLatin1("Result:"), valueResult);
layout->addLayout(formLayout);
Content.emplace_back(taskbox);
// engage the selectionObserver
attachSelection();
// Set selection style
Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::GreedySelection);
if(!App::GetApplication().getActiveTransaction())
App::GetApplication().setActiveTransaction("Add Measurement");
// Call invoke method delayed, otherwise the dialog might not be fully initialized
QTimer::singleShot(0, this, &TaskMeasure::invoke);
}
TaskMeasure::~TaskMeasure(){
Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::NormalSelection);
detachSelection();
qApp->removeEventFilter(this);
}
void TaskMeasure::modifyStandardButtons(QDialogButtonBox* box) {
QPushButton* btn = box->button(QDialogButtonBox::Apply);
btn->setText(tr("Annotate"));
btn->setToolTip(tr("Press the Annotate button to add measurement to the document."));
connect(btn, &QPushButton::released, this, &TaskMeasure::apply);
// Disable button by default
btn->setEnabled(false);
btn = box->button(QDialogButtonBox::Abort);
btn->setText(QString::fromLatin1("Close"));
btn->setToolTip(tr("Press the Close button to exit."));
// Connect reset button
btn = box->button(QDialogButtonBox::Reset);
connect(btn, &QPushButton::released, this, &TaskMeasure::reset);
}
bool canAnnotate(Measure::MeasureBase* obj) {
if (obj == nullptr) {
// null object, can't annotate this
return false;
}
auto vpName = obj->getViewProviderName();
// if there is not a vp, return false
if ((vpName == nullptr) || (vpName[0] == '\0')){
return false;
}
return true;
}
void TaskMeasure::enableAnnotateButton(bool state) {
// if the task ui is not init yet we don't have a button box.
if (!this->buttonBox) {
return;
}
// Enable/Disable annotate button
auto btn = this->buttonBox->button(QDialogButtonBox::Apply);
btn->setEnabled(state);
}
void TaskMeasure::setMeasureObject(Measure::MeasureBase* obj) {
_mMeasureObject = obj;
}
void TaskMeasure::update() {
// Reset selection if the selected object is not valid
for(auto sel : Gui::Selection().getSelection()) {
App::DocumentObject* ob = sel.pObject;
App::DocumentObject* sub = ob->getSubObject(sel.SubName);
std::string mod = Base::Type::getModuleName(sub->getTypeId().getName());
if (!App::MeasureManager::hasMeasureHandler(mod.c_str())) {
Base::Console().Message("No measure handler available for geometry of module: %s\n", mod);
clearSelection();
return;
}
}
valueResult->setText(QString::asprintf("-"));
// Get valid measure type
App::MeasureType *measureType(nullptr);
std::string mode = explicitMode ? modeSwitch->currentText().toStdString() : "";
App::MeasureSelection selection;
for (auto s : Gui::Selection().getSelection()) {
App::SubObjectT sub(s.pObject, s.SubName);
App::MeasureSelectionItem item = { sub, Base::Vector3d(s.x, s.y, s.z) };
selection.push_back(item);
}
auto measureTypes = App::MeasureManager::getValidMeasureTypes(selection, mode);
if (measureTypes.size() > 0) {
measureType = measureTypes.front();
}
if (!measureType) {
// Note: If there's no valid measure type we might just restart the selection,
// however this requires enough coverage of measuretypes that we can access all of them
// std::tuple<std::string, std::string> sel = selection.back();
// clearSelection();
// addElement(measureModule.c_str(), get<0>(sel).c_str(), get<1>(sel).c_str());
// Reset measure object
if (!explicitMode) {
setModeSilent(nullptr);
}
removeObject();
enableAnnotateButton(false);
return;
}
// Update tool mode display
setModeSilent(measureType);
if (!_mMeasureObject || measureType->measureObject != _mMeasureObject->getTypeId().getName()) {
// we don't already have a measureobject or it isn't the same type as the new one
removeObject();
App::Document *doc = App::GetApplication().getActiveDocument();
if (measureType->isPython) {
Base::PyGILStateLocker lock;
auto pyMeasureClass = measureType->pythonClass;
// Create a MeasurePython instance
auto featurePython = doc->addObject("Measure::MeasurePython", measureType->label.c_str());
setMeasureObject((Measure::MeasureBase*)featurePython);
// Create an instance of the pyMeasureClass, the classe's initializer sets the object as proxy
Py::Tuple args(1);
args.setItem(0, Py::asObject(featurePython->getPyObject()));
PyObject_CallObject(pyMeasureClass, args.ptr());
}
else {
// Create measure object
setMeasureObject(
(Measure::MeasureBase*)doc->addObject(measureType->measureObject.c_str(), measureType->label.c_str())
);
}
}
// we have a valid measure object so we can enable the annotate button
enableAnnotateButton(true);
// Fill measure object's properties from selection
_mMeasureObject->parseSelection(selection);
// Get result
valueResult->setText(_mMeasureObject->getResultString());
}
void TaskMeasure::close(){
Control().closeDialog();
}
void ensureGroup(Measure::MeasureBase* measurement) {
// Ensure measurement object is part of the measurements group
const char* measurementGroupName = "Measurements";
if (measurement == nullptr) {
return;
}
App::Document* doc = App::GetApplication().getActiveDocument();
App::DocumentObject* obj = doc->getObject(measurementGroupName);
if (!obj || !obj->isValid()) {
obj = doc->addObject("App::DocumentObjectGroup", measurementGroupName);
}
auto group = static_cast<App::DocumentObjectGroup*>(obj);
group->addObject(measurement);
}
// Runs after the dialog is created
void TaskMeasure::invoke() {
update();
}
bool TaskMeasure::apply(){
ensureGroup(_mMeasureObject);
_mMeasureObject = nullptr;
reset();
// Commit transaction
App::GetApplication().closeActiveTransaction();
App::GetApplication().setActiveTransaction("Add Measurement");
return false;
}
bool TaskMeasure::reject(){
removeObject();
close();
// Abort transaction
App::GetApplication().closeActiveTransaction(true);
return false;
}
void TaskMeasure::reset() {
// Reset tool state
this->clearSelection();
// Should the explicit mode also be reset?
// setModeSilent(nullptr);
// explicitMode = false;
this->update();
}
void TaskMeasure::removeObject() {
if (_mMeasureObject == nullptr) {
return;
}
if (_mMeasureObject->isRemoving() ) {
return;
}
_mMeasureObject->getDocument()->removeObject (_mMeasureObject->getNameInDocument());
setMeasureObject(nullptr);
}
bool TaskMeasure::hasSelection(){
return !Gui::Selection().getSelection().empty();
}
void TaskMeasure::clearSelection(){
Gui::Selection().clearSelection();
}
void TaskMeasure::onSelectionChanged(const Gui::SelectionChanges& msg)
{
// Skip non-relevant events
if (msg.Type != SelectionChanges::AddSelection && msg.Type != SelectionChanges::RmvSelection
&& msg.Type != SelectionChanges::SetSelection && msg.Type != SelectionChanges::ClrSelection) {
return;
}
update();
}
bool TaskMeasure::eventFilter(QObject* obj, QEvent* event) {
if (event->type() == QEvent::KeyPress) {
auto keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Escape) {
if (this->hasSelection()) {
this->reset();
} else {
this->reject();
}
return true;
}
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
this->apply();
return true;
}
}
return TaskDialog::eventFilter(obj, event);
}
void TaskMeasure::onModeChanged(int index) {
explicitMode = (index != 0);
this->update();
}
void TaskMeasure::setModeSilent(App::MeasureType* mode) {
modeSwitch->blockSignals(true);
if (mode == nullptr) {
modeSwitch->setCurrentIndex(0);
}
else {
modeSwitch->setCurrentText(QString::fromLatin1(mode->label.c_str()));
}
modeSwitch->blockSignals(false);
}
// Get explicitly set measure type from the mode switch
App::MeasureType* TaskMeasure::getMeasureType() {
for (App::MeasureType* mType : App::MeasureManager::getMeasureTypes()) {
if (mType->label.c_str() == modeSwitch->currentText().toLatin1()) {
return mType;
}
}
return nullptr;
}

87
src/Gui/TaskMeasure.h Normal file
View File

@@ -0,0 +1,87 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 <qcolumnview.h>
#include <QString>
#include <QComboBox>
#include <QLineEdit>
#include <App/Application.h>
#include <App/MeasureManager.h>
#include <Mod/Measure/App/MeasureBase.h>
#include "TaskView/TaskDialog.h"
#include "TaskView/TaskView.h"
#include "Selection.h"
namespace Gui {
class TaskMeasure : public TaskView::TaskDialog, public Gui::SelectionObserver {
public:
TaskMeasure();
~TaskMeasure() override;
void modifyStandardButtons(QDialogButtonBox* box) override;
QDialogButtonBox::StandardButtons getStandardButtons() const override {
return QDialogButtonBox::Apply | QDialogButtonBox::Abort | QDialogButtonBox::Reset;
}
void invoke();
void update();
void close();
bool apply();
bool reject() override;
void reset();
bool hasSelection();
void clearSelection();
bool eventFilter(QObject* obj, QEvent* event) override;
void setMeasureObject(Measure::MeasureBase* obj);
private:
QColumnView* dialog{nullptr};
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
Measure::MeasureBase *_mMeasureObject = nullptr;
QLineEdit* valueResult{nullptr};
QLabel* labelResult{nullptr};
QComboBox* modeSwitch{nullptr};
void removeObject();
void onModeChanged(int index);
void setModeSilent(App::MeasureType* mode);
App::MeasureType* getMeasureType();
void enableAnnotateButton(bool state);
// List of measure types
std::vector<App::DocumentObject> measureObjects;
// Stores if the mode is explicitly set by the user or implicitly through the selection
bool explicitMode = false;
};
} // namespace Gui

View File

@@ -814,7 +814,7 @@ ToolBarItem* StdWorkbench::setupToolBars() const
view->setCommand("View");
*view << "Std_ViewFitAll" << "Std_ViewFitSelection" << "Std_ViewGroup"
<< "Separator" << "Std_DrawStyle" << "Std_TreeViewActions"
<< "Separator" << "Std_MeasureDistance";
<< "Separator" << "Std_MeasureDistance" << "Std_Measure";
// Individual views
auto individualViews = new ToolBarItem(root, ToolBarItem::DefaultVisibility::Hidden);

View File

@@ -122,8 +122,11 @@ if(BUILD_SURFACE)
add_subdirectory(Surface)
endif(BUILD_SURFACE)
if(BUILD_TECHDRAW)
if(BUILD_MEASURE)
add_subdirectory(Measure)
endif(BUILD_MEASURE)
if(BUILD_TECHDRAW)
add_subdirectory(TechDraw)
endif(BUILD_TECHDRAW)

View File

@@ -22,14 +22,38 @@
#include "PreCompiled.h"
#include <App/MeasureManager.h>
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Mod/Part/App/MeasureInfo.h>
#include <Mod/Part/App/MeasureClient.h>
#include "Measurement.h"
#include "MeasurementPy.h"
// unified measurement facility
#include "MeasureBase.h"
#include "MeasureBasePy.h"
#include "MeasureAngle.h"
#include "MeasureDistance.h"
#include "MeasurePosition.h"
#include "MeasureLength.h"
#include "MeasureArea.h"
#include "MeasureRadius.h"
namespace Measure {
// explicit template instantiations
template class MeasureExport MeasureBaseExtendable<Part::MeasureAngleInfo>;
template class MeasureExport MeasureBaseExtendable<Part::MeasureAreaInfo>;
template class MeasureExport MeasureBaseExtendable<Part::MeasureDistanceInfo>;
template class MeasureExport MeasureBaseExtendable<Part::MeasureLengthInfo>;
template class MeasureExport MeasureBaseExtendable<Part::MeasurePositionInfo>;
template class MeasureExport MeasureBaseExtendable<Part::MeasureRadiusInfo>;
class Module : public Py::ExtensionModule<Module>
{
public:
@@ -48,6 +72,7 @@ PyObject* initModule()
} // namespace Measure
using namespace Measure;
/* Python entry */
PyMOD_INIT_FUNC(Measure)
@@ -62,14 +87,103 @@ PyMOD_INIT_FUNC(Measure)
}
PyObject* mod = Measure::initModule();
// Add Types to module
Base::Interpreter().addType(&Measure::MeasurementPy ::Type,mod,"Measurement");
Base::Console().Log("Loading Inspection module... done\n");
Measure::Measurement ::init();
Base::Interpreter().addType(&Measure::MeasurementPy::Type, mod, "Measurement");
Base::Interpreter().addType(&Measure::MeasureBasePy::Type, mod, "MeasureBase");
Measure::Measurement ::init();
// umf classes
Measure::MeasureBase ::init();
Measure::MeasurePython ::init();
Measure::MeasureAngle ::init();
Measure::MeasureDistance ::init();
Measure::MeasurePosition ::init();
Measure::MeasureLength ::init();
Measure::MeasureArea ::init();
Measure::MeasureRadius ::init();
// Add fundamental umf Measure Types
App::MeasureManager::addMeasureType("DISTANCE",
"Distance",
"Measure::MeasureDistance",
MeasureDistance::isValidSelection,
MeasureDistance::isPrioritizedSelection
);
App::MeasureManager::addMeasureType(
"ANGLE",
"Angle",
"Measure::MeasureAngle",
MeasureAngle::isValidSelection,
MeasureAngle::isPrioritizedSelection
);
App::MeasureManager::addMeasureType(
"LENGTH",
"Length",
"Measure::MeasureLength",
MeasureLength::isValidSelection,
nullptr
);
App::MeasureManager::addMeasureType(
"POSITION",
"Position",
"Measure::MeasurePosition",
MeasurePosition::isValidSelection,
nullptr
);
App::MeasureManager::addMeasureType(
"AREA",
"Area",
"Measure::MeasureArea",
MeasureArea::isValidSelection,
nullptr
);
App::MeasureManager::addMeasureType(
"RADIUS",
"Radius",
"Measure::MeasureRadius",
MeasureRadius::isValidSelection,
MeasureRadius::isPrioritizedSelection
);
// load measure callbacks from Part module
auto lengthList = Part::MeasureClient::reportLengthCB();
for (auto& entry : lengthList) {
MeasureBaseExtendable<Part::MeasureLengthInfo>::addGeometryHandler(entry.m_module, entry.m_callback);
}
auto angleList = Part::MeasureClient::reportAngleCB();
for (auto& entry : angleList) {
MeasureBaseExtendable<Part::MeasureAngleInfo>::addGeometryHandler(entry.m_module, entry.m_callback);
}
auto areaList = Part::MeasureClient::reportAreaCB();
for (auto& entry : areaList) {
MeasureBaseExtendable<Part::MeasureAreaInfo>::addGeometryHandler(entry.m_module, entry.m_callback);
}
auto distanceList = Part::MeasureClient::reportDistanceCB();
for (auto& entry : distanceList) {
MeasureBaseExtendable<Part::MeasureDistanceInfo>::addGeometryHandler(entry.m_module, entry.m_callback);
}
auto positionList = Part::MeasureClient::reportPositionCB();
for (auto& entry : positionList) {
MeasureBaseExtendable<Part::MeasurePositionInfo>::addGeometryHandler(entry.m_module, entry.m_callback);
}
auto radiusList = Part::MeasureClient::reportRadiusCB();
for (auto& entry : radiusList) {
MeasureBaseExtendable<Part::MeasureRadiusInfo>::addGeometryHandler(entry.m_module, entry.m_callback);
}
Base::Console().Log("Loading Measure module... done\n");
PyMOD_Return(mod);
}
// debug print for sketchsolv
void debugprint(const std::string& s)
void debugprint(const std::string& text)
{
Base::Console().Log("%s", s.c_str());
Base::Console().Log("%s", text.c_str());
}

View File

@@ -17,19 +17,43 @@ set(Measure_LIBS
)
generate_from_xml(MeasurementPy)
generate_from_xml(MeasureBasePy)
SET(Python_SRCS
MeasurementPy.xml
MeasurementPyImp.cpp
MeasureBasePy.xml
MeasureBasePyImp.cpp
)
SOURCE_GROUP("Python" FILES ${Python_SRCS})
SET(MeasureModule_SRCS
AppMeasure.cpp
Measurement.cpp
Measurement.h
PreCompiled.cpp
PreCompiled.h
AppMeasure.cpp
# original service routines
Measurement.cpp
Measurement.h
# umf
MeasureBase.cpp
MeasureBase.h
MeasureAngle.cpp
MeasureAngle.h
MeasureDistance.cpp
MeasureDistance.h
MeasurePosition.cpp
MeasurePosition.h
MeasureLength.cpp
MeasureLength.h
MeasureArea.cpp
MeasureArea.h
MeasureRadius.cpp
MeasureRadius.h
Preferences.cpp
Preferences.h
)
SOURCE_GROUP("Module" FILES ${MeasureModule_SRCS})
@@ -45,18 +69,9 @@ if(FREECAD_USE_PCH)
ADD_MSVC_PRECOMPILED_HEADER(Measure PreCompiled.h PreCompiled.cpp Measure_CPP_SRCS)
endif(FREECAD_USE_PCH)
SET(Measure_Scripts
../Init.py
)
add_library(Measure SHARED ${Measure_SRCS} ${Measure_Scripts})
add_library(Measure SHARED ${Measure_SRCS})
target_link_libraries(Measure ${Measure_LIBS})
fc_target_copy_resource_flat(Measure
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/Mod/Measure
${Measure_Scripts})
#if(MSVC)
# set_target_properties(Measure PROPERTIES SUFFIX ".pyd")
# set_target_properties(Measure PROPERTIES DEBUG_OUTPUT_NAME "Measure_d")

View File

@@ -0,0 +1,264 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/PropertyContainer.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/MeasureManager.h>
#include <Base/Tools.h>
#include <Base/Precision.h>
#include "MeasureAngle.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasureAngle, Measure::MeasureBase)
MeasureAngle::MeasureAngle()
{
ADD_PROPERTY_TYPE(Element1,(nullptr), "Measurement", App::Prop_None, "First element of the measurement");
Element1.setScope(App::LinkScope::Global);
Element1.setAllowExternal(true);
ADD_PROPERTY_TYPE(Element2,(nullptr), "Measurement", App::Prop_None, "Second element of the measurement");
Element2.setScope(App::LinkScope::Global);
Element2.setAllowExternal(true);
ADD_PROPERTY_TYPE(Angle,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),
"Angle between the two elements");
Angle.setUnit(Base::Unit::Angle);
}
MeasureAngle::~MeasureAngle() = default;
bool MeasureAngle::isValidSelection(const App::MeasureSelection& selection)
{
if (selection.size() != 2) {
return false;
}
for (auto element : selection) {
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::INVALID) {
return false;
}
if (!(type == App::MeasureElementType::LINE ||
type == App::MeasureElementType::PLANE ||
type == App::MeasureElementType::LINESEGMENT)) {
return false;
}
}
return true;
}
bool MeasureAngle::isPrioritizedSelection(const App::MeasureSelection& selection)
{
if (selection.size() != 2) {
return false;
}
// Check if the two elements are parallel
auto element1 = selection.at(0);
auto objT1 = element1.object;
App::DocumentObject* ob1 = objT1.getObject();
std::string sub1 = objT1.getSubName();
Base::Vector3d vec1;
getVec(*ob1, sub1, vec1);
auto element2 = selection.at(1);
auto objT2 = element2.object;
App::DocumentObject* ob2 = objT2.getObject();
std::string sub2 = objT2.getSubName();
Base::Vector3d vec2;
getVec(*ob2, sub2, vec2);
double angle = std::fmod(vec1.GetAngle(vec2), D_PI);
return angle > Base::Precision::Angular();
}
void MeasureAngle::parseSelection(const App::MeasureSelection& selection) {
assert(selection.size() >= 2);
auto element1 = selection.at(0);
auto objT1 = element1.object;
App::DocumentObject* ob1 = objT1.getObject();
const std::vector<std::string> elems1 = {objT1.getSubName()};
Element1.setValue(ob1, elems1);
auto element2 = selection.at(1);
auto objT2 = element2.object;
App::DocumentObject* ob2 = objT2.getObject();
const std::vector<std::string> elems2 = {objT2.getSubName()};
Element2.setValue(ob2, elems2);
}
bool MeasureAngle::getVec(App::DocumentObject& ob, std::string& subName, Base::Vector3d& vecOut) {
const char* className = ob.getSubObject(subName.c_str())->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
auto handler = getGeometryHandler(mod);
App::SubObjectT subject{&ob, subName.c_str()};
auto info = handler(subject);
auto angleInfo = std::dynamic_pointer_cast<Part::MeasureAngleInfo>(info);
vecOut = angleInfo->orientation;
return true;
}
Base::Vector3d MeasureAngle::getLoc(App::DocumentObject& ob, std::string& subName)
{
const char* className = ob.getSubObject(subName.c_str())->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return Base::Vector3d();
}
auto handler = getGeometryHandler(mod);
App::SubObjectT subject{&ob, subName.c_str()};
auto info = handler(subject);
auto angleInfo = std::dynamic_pointer_cast<Part::MeasureAngleInfo>(info);
return angleInfo->position;
}
gp_Vec MeasureAngle::vector1() {
App::DocumentObject* ob = Element1.getValue();
std::vector<std::string> subs = Element1.getSubValues();
if (!ob || !ob->isValid() || subs.empty() ) {
return {};
}
Base::Vector3d vec;
getVec(*ob, subs.at(0), vec);
return gp_Vec(vec.x, vec.y, vec.z);
}
gp_Vec MeasureAngle::vector2() {
App::DocumentObject* ob = Element2.getValue();
std::vector<std::string> subs = Element2.getSubValues();
if (!ob || !ob->isValid() || subs.empty() ) {
return gp_Vec();
}
Base::Vector3d vec;
getVec(*ob, subs.at(0), vec);
return gp_Vec(vec.x, vec.y, vec.z);
}
gp_Vec MeasureAngle::location1() {
App::DocumentObject* ob = Element1.getValue();
std::vector<std::string> subs = Element1.getSubValues();
if (!ob || !ob->isValid() || subs.empty() ) {
return {};
}
auto temp = getLoc(*ob, subs.at(0));
return {temp.x, temp.y, temp.z};
}
gp_Vec MeasureAngle::location2() {
App::DocumentObject* ob = Element2.getValue();
std::vector<std::string> subs = Element2.getSubValues();
if (!ob || !ob->isValid() || subs.empty() ) {
return {};
}
auto temp = getLoc(*ob, subs.at(0));
return {temp.x, temp.y, temp.z};
}
App::DocumentObjectExecReturn *MeasureAngle::execute()
{
App::DocumentObject* ob1 = Element1.getValue();
std::vector<std::string> subs1 = Element1.getSubValues();
App::DocumentObject* ob2 = Element2.getValue();
std::vector<std::string> subs2 = Element2.getSubValues();
if (!ob1 || !ob1->isValid() || !ob2 || !ob2->isValid()) {
return new App::DocumentObjectExecReturn("Submitted object(s) is not valid");
}
if (subs1.empty() || subs2.empty()) {
return new App::DocumentObjectExecReturn("No geometry element picked");
}
Base::Vector3d vec1;
getVec(*ob1, subs1.at(0), vec1);
Base::Vector3d vec2;
getVec(*ob2, subs2.at(0), vec2);
Angle.setValue(Base::toDegrees(vec1.GetAngle(vec2)));
return DocumentObject::StdReturn;
}
void MeasureAngle::onChanged(const App::Property* prop)
{
if (prop == &Element1 || prop == &Element2) {
if (!isRestoring()) {
App::DocumentObjectExecReturn *ret = recompute();
delete ret;
}
}
DocumentObject::onChanged(prop);
}
//! Return the object we are measuring
//! used by the viewprovider in determining visibility
std::vector<App::DocumentObject*> MeasureAngle::getSubject() const
{
return {Element1.getValue()};
}

View File

@@ -0,0 +1,93 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 APP_MEASUREANGLE_H
#define APP_MEASUREANGLE_H
#include <Mod/Measure/MeasureGlobal.h>
#include <gp_Vec.hxx>
#include <App/PropertyGeo.h>
#include <App/PropertyLinks.h>
#include <App/PropertyUnits.h>
#include <Base/Vector3D.h>
#include <Mod/Part/App/MeasureInfo.h>
#include "MeasureBase.h"
namespace Measure
{
class MeasureExport MeasureAngle : public Measure::MeasureBaseExtendable<Part::MeasureAngleInfo>
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureAngle);
public:
/// Constructor
MeasureAngle();
~MeasureAngle() override;
App::PropertyLinkSub Element1;
App::PropertyLinkSub Element2;
App::PropertyAngle Angle;
App::DocumentObjectExecReturn *execute() override;
const char* getViewProviderName() const override {
return "MeasureGui::ViewProviderMeasureAngle";
}
static bool isValidSelection(const App::MeasureSelection& selection);
static bool isPrioritizedSelection(const App::MeasureSelection& selection);
void parseSelection(const App::MeasureSelection& selection) override;
std::vector<std::string> getInputProps() override {return {"Element1", "Element2"};}
App::Property* getResultProp() override {return &this->Angle;}
// Return the object we are measuring
std::vector<App::DocumentObject*> getSubject() const override;
static bool getVec(App::DocumentObject& ob, std::string& subName, Base::Vector3d& vecOut);
Base::Vector3d getLoc(App::DocumentObject& ob, std::string& subName);
// Orientation Vectors
gp_Vec vector1();
gp_Vec vector2();
// Location Vectors
gp_Vec location1();
gp_Vec location2();
private:
void onChanged(const App::Property* prop) override;
};
} //namespace Measure
#endif // APP_MEASUREANGLE_H

View File

@@ -0,0 +1,181 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/Application.h>
#include <App/MeasureManager.h>
#include <App/Document.h>
#include "MeasureArea.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasureArea, Measure::MeasureBase)
MeasureArea::MeasureArea()
{
ADD_PROPERTY_TYPE(Elements,(nullptr), "Measurement", App::Prop_None, "Element to get the area from");
Elements.setScope(App::LinkScope::Global);
Elements.setAllowExternal(true);
ADD_PROPERTY_TYPE(Area,(0.0), "Measurement", App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),
"Area of element");
}
MeasureArea::~MeasureArea() = default;
bool MeasureArea::isValidSelection(const App::MeasureSelection& selection){
if (selection.empty()) {
return false;
}
for (auto element : selection) {
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::INVALID) {
return false;
}
// TODO: Also support Cylinder & Volume?
if ((type != App::MeasureElementType::PLANE && type != App::MeasureElementType::CYLINDER)) {
return false;
}
}
return true;
}
void MeasureArea::parseSelection(const App::MeasureSelection& selection) {
// Set properties from selection, method is only invoked when isValid Selection returns true
std::vector<App::DocumentObject*> objects;
std::vector<std::string> subElements;
for (auto element : selection) {
auto objT = element.object;
objects.push_back(objT.getObject());
subElements.push_back(objT.getSubName());
}
Elements.setValues(objects, subElements);
}
App::DocumentObjectExecReturn *MeasureArea::execute()
{
recalculateArea();
return DocumentObject::StdReturn;
}
void MeasureArea::recalculateArea()
{
const std::vector<App::DocumentObject*>& objects = Elements.getValues();
const std::vector<std::string>& subElements = Elements.getSubValues();
double result(0.0);
// Loop through Elements and call the valid geometry handler
for (std::vector<App::DocumentObject*>::size_type i=0; i<objects.size(); i++) {
App::DocumentObject *object = objects.at(i);
std::string subElement = subElements.at(i);
// Get the Geometry handler based on the module
const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName();
const std::string& mod = Base::Type::getModuleName(className);
auto handler = getGeometryHandler(mod);
if (!handler) {
throw Base::RuntimeError("No geometry handler available for submitted element type");
}
App::SubObjectT subject{object, subElement.c_str()};
auto info = handler(subject);
auto areaInfo = std::dynamic_pointer_cast<Part::MeasureAreaInfo>(info);
result += areaInfo->area;
}
Area.setValue(result);
}
void MeasureArea::onChanged(const App::Property* prop)
{
if (isRestoring() || isRemoving()) {
return;
}
if (prop == &Elements) {
recalculateArea();
}
MeasureBase::onChanged(prop);
}
Base::Placement MeasureArea::getPlacement() {
const std::vector<App::DocumentObject*>& objects = Elements.getValues();
const std::vector<std::string>& subElements = Elements.getSubValues();
if (objects.empty() || subElements.empty()) {
return Base::Placement();
}
App::DocumentObject* object = objects.front();
std::string subElement = subElements.front();
const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName();
const std::string& mod = Base::Type::getModuleName(className);
auto handler = getGeometryHandler(mod);
if (!handler) {
throw Base::RuntimeError("No geometry handler available for submitted element type");
}
App::SubObjectT subject{object, subElement.c_str()};
auto info = handler(subject);
auto areaInfo = std::dynamic_pointer_cast<Part::MeasureAreaInfo>(info);
return areaInfo->placement;
}
//! Return the object we are measuring
//! used by the viewprovider in determining visibility
std::vector<App::DocumentObject*> MeasureArea::getSubject() const
{
return Elements.getValues();
}

View File

@@ -0,0 +1,86 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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_MEASUREAREA_H
#define MEASURE_MEASUREAREA_H
#include <Mod/Measure/MeasureGlobal.h>
#include <string>
#include <App/PropertyLinks.h>
#include <App/PropertyUnits.h>
#include <App/GeoFeature.h>
#include <Mod/Part/App/MeasureInfo.h>
#include "MeasureBase.h"
namespace Measure
{
class MeasureExport MeasureArea : public Measure::MeasureBaseExtendable<Part::MeasureAreaInfo>
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureArea);
public:
/// Constructor
MeasureArea();
~MeasureArea() override;
App::PropertyLinkSubList Elements;
App::PropertyArea Area;
App::DocumentObjectExecReturn *execute() override;
void recalculateArea();
const char* getViewProviderName() const override {
return "MeasureGui::ViewProviderMeasure";
}
static bool isValidSelection(const App::MeasureSelection& selection);
void parseSelection(const App::MeasureSelection& selection) override;
std::vector<std::string> getInputProps() override {return {"Elements"};}
App::Property* getResultProp() override {return &this->Area;}
// Return a placement for the viewprovider, just use the first element for now
Base::Placement getPlacement() override;
// Return the object we are measuring
std::vector<App::DocumentObject*> getSubject() const override;
private:
void onChanged(const App::Property* prop) override;
};
} //namespace Measure
#endif // MEASURE_MEASUREAREA_H

View File

@@ -0,0 +1,211 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/PropertyGeo.h>
#include <Base/PlacementPy.h>
#include <App/FeaturePythonPyImp.h>
#include <App/DocumentObjectPy.h>
#include "MeasureBase.h"
// Generated from MeasureBasePy.xml
#include "MeasureBasePy.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasureBase, App::DocumentObject)
MeasureBase::MeasureBase() {
ADD_PROPERTY_TYPE(Placement, (Base::Placement()), nullptr, App::PropertyType(App::Prop_ReadOnly|App::Prop_Output|App::Prop_NoRecompute), "Visual placement of the measurement");
}
PyObject *MeasureBase::getPyObject(void)
{
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new MeasureBasePy(this), true);
}
return Py::new_reference_to(PythonObject);
}
Py::Object MeasureBase::getProxyObject() const {
Base::PyGILStateLocker lock;
App::Property* prop = this->getPropertyByName("Proxy");
if (!prop) {
return Py::None();
}
return dynamic_cast<App::PropertyPythonObject*>(prop)->getValue();
};
std::vector<App::DocumentObject*> MeasureBase::getSubject() const {
Base::PyGILStateLocker lock;
Py::Object proxy = getProxyObject();
// Pass the feture object to the proxy
Py::Tuple args(1);
args.setItem(0, Py::Object(const_cast<MeasureBase*>(this)->getPyObject()));
Py::Object ret;
try {
ret = proxy.callMemberFunction("getSubject", args);
} catch (Py::Exception&) {
Base::PyException e;
e.ReportException();
return {};
}
Py::Sequence retTuple(ret);
std::vector<App::DocumentObject*> retVec;
for (Py::Object o : retTuple) {
retVec.push_back(static_cast<App::DocumentObjectPy*>(o.ptr())->getDocumentObjectPtr());
}
return retVec;
};
void MeasureBase::parseSelection(const App::MeasureSelection& selection) {
Base::PyGILStateLocker lock;
Py::Object proxy = getProxyObject();
// Convert selection to python list
Py::Tuple selectionPy = App::MeasureManager::getSelectionPy(selection);
Py::Tuple args(2);
// Pass the feture object to the proxy
args.setItem(0, Py::Object(const_cast<MeasureBase*>(this)->getPyObject()));
args.setItem(1, selectionPy);
// Call the parseSelection method of the proxy object
try {
proxy.callMemberFunction("parseSelection", args);
} catch (Py::Exception&) {
Base::PyException e;
e.ReportException();
}
}
std::vector<std::string> MeasureBase::getInputProps() {
Base::PyGILStateLocker lock;
Py::Object proxy = getProxyObject();
if (proxy.isNone()) {
return {};
}
Py::Object ret;
try {
ret = proxy.callMemberFunction("getInputProps");
} catch (Py::Exception&) {
Base::PyException e;
e.ReportException();
return {};
}
Py::Sequence propsPy(ret);
// Get cpp vector from propsPy
std::vector<std::string> props;
for (Py::Object o : propsPy) {
props.push_back(o.as_string());
}
return props;
}
QString MeasureBase::getResultString() {
Py::Object proxy = getProxyObject();
Base::PyGILStateLocker lock;
if (!proxy.isNone()) {
// Pass the feture object to the proxy
Py::Tuple args(1);
args.setItem(0, Py::Object(const_cast<MeasureBase*>(this)->getPyObject()));
Py::Object ret;
try {
ret = proxy.callMemberFunction("getResultString", args);
} catch (Py::Exception&) {
Base::PyException e;
e.ReportException();
return QString();
}
return QString::fromStdString(ret.as_string());
}
App::Property* prop = getResultProp();
if (prop == nullptr) {
return QString();
}
if (prop->isDerivedFrom(App::PropertyQuantity::getClassTypeId())) {
return static_cast<App::PropertyQuantity*>(prop)->getQuantityValue().getUserString();
}
return QString();
}
void MeasureBase::onDocumentRestored() {
// Force recompute the measurement
recompute();
}
Base::Placement MeasureBase::getPlacement() {
return this->Placement.getValue();
}
// Python Drawing feature ---------------------------------------------------------
namespace App {
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(Measure::MeasurePython, Measure::MeasureBase)
template<> const char* Measure::MeasurePython::getViewProviderName(void) const {
return "MeasureGui::ViewProviderMeasure";
}
template<> PyObject* Measure::MeasurePython::getPyObject() {
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new FeaturePythonPyT<Measure::MeasureBasePy>(this),true);
}
return Py::new_reference_to(PythonObject);
}
/// @endcond
// explicit template instantiation
template class MeasureExport FeaturePythonT<Measure::MeasureBase>;
}

View File

@@ -0,0 +1,131 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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_MEASUREBASE_H
#define MEASURE_MEASUREBASE_H
#include <Mod/Measure/MeasureGlobal.h>
#include <memory>
#include <QString>
#include <App/DocumentObject.h>
#include <App/MeasureManager.h>
#include <App/DocumentObserver.h>
#include <App/PropertyStandard.h>
#include <App/PropertyUnits.h>
#include <App/FeaturePython.h>
#include <Base/Quantity.h>
#include <Base/Placement.h>
#include <Base/Interpreter.h>
#include <Mod/Part/App/MeasureInfo.h>
#include <Mod/Part/App/MeasureClient.h> // needed?
namespace Measure
{
class MeasureExport MeasureBase : public App::DocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureBase);
public:
MeasureBase();
~MeasureBase() override = default;
App::PropertyPlacement Placement;
// boost::signals2::signal<void (const MeasureBase*)> signalGuiInit;
//return PyObject as MeasureBasePy
PyObject *getPyObject() override;
// Initialize measurement properties from selection
virtual void parseSelection(const App::MeasureSelection& selection);
virtual QString getResultString();
virtual std::vector<std::string> getInputProps();
virtual App::Property* getResultProp() {return {};};
virtual Base::Placement getPlacement();
// Return the objects that are measured
virtual std::vector<App::DocumentObject*> getSubject() const;
private:
Py::Object getProxyObject() const;
protected:
void onDocumentRestored() override;
};
// Create a scriptable object based on MeasureBase
using MeasurePython = App::FeaturePythonT<MeasureBase>;
template <typename T>
class MeasureExport MeasureBaseExtendable : public MeasureBase
{
using GeometryHandler = std::function<Part::MeasureInfoPtr (const App::SubObjectT&)>;
using HandlerMap = std::map<std::string, GeometryHandler>;
public:
static void addGeometryHandler(const std::string& module, GeometryHandler callback) {
_mGeometryHandlers[module] = callback;
}
static GeometryHandler getGeometryHandler(const std::string& module) {
if (!hasGeometryHandler(module)) {
return {};
}
return _mGeometryHandlers[module];
}
static void addGeometryHandlers(const std::vector<std::string>& modules, GeometryHandler callback){
// TODO: this will replace a callback with a later one. Should we check that there isn't already a
// handler defined for this module?
for (auto& mod : modules) {
_mGeometryHandlers[mod] = callback;
}
}
static bool hasGeometryHandler(const std::string& module) {
return (_mGeometryHandlers.count(module) > 0);
}
private:
inline static HandlerMap _mGeometryHandlers = MeasureBaseExtendable<T>::HandlerMap();
};
} //namespace Measure
#endif // MEASURE_MEASUREBASE_H

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="DocumentObjectPy"
Name="MeasureBasePy"
Twin="MeasureBase"
TwinPointer="MeasureBase"
Include="Mod/Measure/App/MeasureBase.h"
Namespace="Measure"
FatherInclude="App/DocumentObjectPy.h"
FatherNamespace="App"
Constructor="true">
<Documentation>
<Author Licence="LGPL" Name="David Friedli(hlorus)" EMail="david@friedli-be.ch" />
<UserDocu>User documentation here
</UserDocu>
<DeveloperDocu>Developer documentation here</DeveloperDocu>
</Documentation>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,40 @@
#include "PreCompiled.h"
#include "MeasureBase.h"
// Inclusion of the generated files (generated out of MeasureBasePy.xml)
#include "MeasureBasePy.h"
#include "MeasureBasePy.cpp"
#include <Base/GeometryPyCXX.h>
// returns a string which represents the object e.g. when printed in python
std::string MeasureBasePy::representation() const
{
return "<Measure::MeasureBase>";
}
PyObject *MeasureBasePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
{
// create a new instance of MeasureBasePy and the Twin object
return new MeasureBasePy(new MeasureBase);
}
// constructor method
int MeasureBasePy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
PyObject *MeasureBasePy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int MeasureBasePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -0,0 +1,231 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/PropertyContainer.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/MeasureManager.h>
#include <Base/Tools.h>
#include <BRepExtrema_DistShapeShape.hxx>
#include "MeasureDistance.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasureDistance, Measure::MeasureBase)
MeasureDistance::MeasureDistance()
{
ADD_PROPERTY_TYPE(Element1,(nullptr), "Measurement", App::Prop_None, "First element of the measurement");
Element1.setScope(App::LinkScope::Global);
Element1.setAllowExternal(true);
ADD_PROPERTY_TYPE(Element2,(nullptr), "Measurement", App::Prop_None, "Second element of the measurement");
Element2.setScope(App::LinkScope::Global);
Element2.setAllowExternal(true);
ADD_PROPERTY_TYPE(Distance,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),
"Distance between the two elements");
Distance.setUnit(Base::Unit::Length);
ADD_PROPERTY_TYPE(Position1,(Base::Vector3d(0.0,0.0,0.0)),"Measurement", App::Prop_Hidden, "Position1");
ADD_PROPERTY_TYPE(Position2,(Base::Vector3d(0.0,1.0,0.0)),"Measurement", App::Prop_Hidden, "Position2");
}
MeasureDistance::~MeasureDistance() = default;
bool MeasureDistance::isValidSelection(const App::MeasureSelection& selection){
if (selection.size() != 2) {
return false;
}
for (auto element : selection) {
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::INVALID) {
return false;
}
if (
type != App::MeasureElementType::POINT &&
type != App::MeasureElementType::LINE &&
type != App::MeasureElementType::LINESEGMENT &&
type != App::MeasureElementType::CIRCLE &&
type != App::MeasureElementType::ARC &&
type != App::MeasureElementType::CURVE &&
type != App::MeasureElementType::PLANE &&
type != App::MeasureElementType::CYLINDER
) {
return false;
}
}
return true;
}
bool MeasureDistance::isPrioritizedSelection(const App::MeasureSelection& selection) {
(void)selection;
// TODO: Check if Elements ar parallel
// if (selection.size() == 2) {
// return true;
// }
return false;
}
void MeasureDistance::parseSelection(const App::MeasureSelection& selection) {
assert(selection.size() >= 2);
auto element1 = selection.at(0);
auto objT1 = element1.object;
App::DocumentObject* ob1 = objT1.getObject();
const std::vector<std::string> elems1 = {objT1.getSubName()};
Element1.setValue(ob1, elems1);
auto element2 = selection.at(1);
auto objT2 = element2.object;
App::DocumentObject* ob2 = objT2.getObject();
const std::vector<std::string> elems2 = {objT2.getSubName()};
Element2.setValue(ob2, elems2);
}
bool MeasureDistance::getShape(App::PropertyLinkSub* prop, TopoDS_Shape& rShape) {
App::DocumentObject* ob = prop->getValue();
std::vector<std::string> subs = prop->getSubValues();
if (!ob || !ob->isValid() || subs.empty() ) {
return false;
}
std::string subName = subs.at(0);
const char* className = ob->getSubObject(subName.c_str())->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
auto handler = getGeometryHandler(mod);
std::string obName = static_cast<std::string>(ob->getNameInDocument());
App::SubObjectT subject{ob, subName.c_str()};
auto info = handler(subject);
if (!info->valid) {
return false;
}
auto distanceInfo = std::dynamic_pointer_cast<Part::MeasureDistanceInfo>(info);
rShape = *distanceInfo->getShape();
return true;
}
App::DocumentObjectExecReturn *MeasureDistance::execute()
{
App::DocumentObject* ob1 = Element1.getValue();
std::vector<std::string> subs1 = Element1.getSubValues();
App::DocumentObject* ob2 = Element2.getValue();
std::vector<std::string> subs2 = Element2.getSubValues();
if (!ob1 || !ob1->isValid() || !ob2 || !ob2->isValid()) {
return new App::DocumentObjectExecReturn("Submitted object(s) is not valid");
}
if (subs1.empty() || subs2.empty()) {
return new App::DocumentObjectExecReturn("No geometry element picked");
}
// Get both shapes from geometry handler
TopoDS_Shape shape1;
if (!this->getShape(&Element1, shape1)) {
return new App::DocumentObjectExecReturn("Could not get shape");
}
TopoDS_Shape shape2;
if (!this->getShape(&Element2, shape2)) {
return new App::DocumentObjectExecReturn("Could not get shape");
}
// Calculate the extrema
BRepExtrema_DistShapeShape measure(shape1, shape2);
if (!measure.IsDone() || measure.NbSolution() < 1) {
return new App::DocumentObjectExecReturn("Could not get extrema");
}
Distance.setValue(measure.Value());
gp_Pnt p1 = measure.PointOnShape1(1);
Position1.setValue(p1.X(), p1.Y(), p1.Z());
gp_Pnt p2 = measure.PointOnShape2(1);
Position2.setValue(p2.X(), p2.Y(), p2.Z());
return DocumentObject::StdReturn;
}
void MeasureDistance::onChanged(const App::Property* prop)
{
if (prop == &Element1 || prop == &Element2) {
if (!isRestoring()) {
App::DocumentObjectExecReturn *ret = recompute();
delete ret;
}
}
DocumentObject::onChanged(prop);
}
//! Return the object we are measuring
//! used by the viewprovider in determining visibility
std::vector<App::DocumentObject*> MeasureDistance::getSubject() const
{
return {Element1.getValue()};
}

View File

@@ -0,0 +1,87 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 MEASUREAPP_MEASUREDISTANCE_H
#define MEASUREAPP_MEASUREDISTANCE_H
#include <Mod/Measure/MeasureGlobal.h>
#include <TopoDS_Shape.hxx>
#include <App/PropertyGeo.h>
#include <App/PropertyLinks.h>
#include <App/PropertyUnits.h>
#include <Mod/Part/App/MeasureInfo.h>
#include "MeasureBase.h"
namespace Measure
{
class MeasureExport MeasureDistance : public Measure::MeasureBaseExtendable<Part::MeasureDistanceInfo>
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureDistance);
public:
/// Constructor
MeasureDistance();
~MeasureDistance() override;
App::PropertyLinkSub Element1;
App::PropertyLinkSub Element2;
App::PropertyDistance Distance;
// Position properties for the viewprovider
App::PropertyVector Position1;
App::PropertyVector Position2;
App::DocumentObjectExecReturn *execute() override;
const char* getViewProviderName() const override {
return "MeasureGui::ViewProviderMeasureDistance";
}
static bool isValidSelection(const App::MeasureSelection& selection);
static bool isPrioritizedSelection(const App::MeasureSelection& selection);
void parseSelection(const App::MeasureSelection& selection) override;
std::vector<std::string> getInputProps() override {return {"Element1", "Element2"};}
App::Property* getResultProp() override {return &this->Distance;}
bool getShape(App::PropertyLinkSub* prop, TopoDS_Shape& rShape);
// Return the object we are measuring
std::vector<App::DocumentObject*> getSubject() const override;
private:
void onChanged(const App::Property* prop) override;
};
} //namespace Measure
#endif // MEASUREAPP_MEASUREDISTANCE_H

View File

@@ -0,0 +1,184 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/Application.h>
#include <App/Document.h>
#include <App/MeasureManager.h>
#include <Mod/Part/App/PartFeature.h>
#include "MeasureLength.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasureLength, Measure::MeasureBase)
MeasureLength::MeasureLength()
{
ADD_PROPERTY_TYPE(Elements,(nullptr), "Measurement", App::Prop_None, "Elements to get the length from");
Elements.setScope(App::LinkScope::Global);
Elements.setAllowExternal(true);
ADD_PROPERTY_TYPE(Length,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),
"Length of selection");
}
MeasureLength::~MeasureLength() = default;
bool MeasureLength::isValidSelection(const App::MeasureSelection& selection){
if (selection.empty()) {
return false;
}
for (auto element : selection) {
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::INVALID) {
return false;
}
if ((type != App::MeasureElementType::LINESEGMENT && type != App::MeasureElementType::CIRCLE
&& type != App::MeasureElementType::ARC && type != App::MeasureElementType::CURVE)) {
return false;
}
}
return true;
}
void MeasureLength::parseSelection(const App::MeasureSelection& selection) {
// Set properties from selection, method is only invoked when isValid Selection returns true
std::vector<App::DocumentObject*> objects;
std::vector<std::string> subElements;
for (auto element : selection) {
auto objT = element.object;
objects.push_back(objT.getObject());
subElements.push_back(objT.getSubName());
}
Elements.setValues(objects, subElements);
}
App::DocumentObjectExecReturn *MeasureLength::execute()
{
recalculateLength();
return DocumentObject::StdReturn;
}
void MeasureLength::recalculateLength()
{
const std::vector<App::DocumentObject*>& objects = Elements.getValues();
const std::vector<std::string>& subElements = Elements.getSubValues();
double result(0.0);
// Loop through Elements and call the valid geometry handler
for (std::vector<App::DocumentObject*>::size_type i=0; i<objects.size(); i++) {
App::DocumentObject *object = objects.at(i);
std::string subElement = subElements.at(i);
// Get the Geometry handler based on the module
const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName();
const std::string& mod = Base::Type::getModuleName(className);
auto handler = getGeometryHandler(mod);
if (!handler) {
throw Base::RuntimeError("No geometry handler available for submitted element type");
}
App::SubObjectT subject{object, subElement.c_str()};
auto info = handler(subject);
auto lengthInfo = std::dynamic_pointer_cast<Part::MeasureLengthInfo>(info);
result += lengthInfo->length;
}
Length.setValue(result);
}
void MeasureLength::onChanged(const App::Property* prop)
{
if (isRestoring() || isRemoving()) {
return;
}
if (prop == &Elements) {
recalculateLength();
}
MeasureBase::onChanged(prop);
}
Base::Placement MeasureLength::getPlacement() {
const std::vector<App::DocumentObject*>& objects = Elements.getValues();
const std::vector<std::string>& subElements = Elements.getSubValues();
if (!objects.size() || !subElements.size()) {
return Base::Placement();
}
App::DocumentObject* object = objects.front();
std::string subElement = subElements.front();
const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName();
const std::string& mod = Base::Type::getModuleName(className);
auto handler = getGeometryHandler(mod);
if (!handler) {
throw Base::RuntimeError("No geometry handler available for submitted element type");
}
App::SubObjectT subject{object, subElement.c_str()};
auto info = handler(subject);
auto lengthInfo = std::dynamic_pointer_cast<Part::MeasureLengthInfo>(info);
return lengthInfo->placement;
}
//! Return the object we are measuring
//! used by the viewprovider in determining visibility
std::vector<App::DocumentObject*> MeasureLength::getSubject() const
{
return Elements.getValues();
}

View File

@@ -0,0 +1,81 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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_MEASURELENGTH_H
#define MEASURE_MEASURELENGTH_H
#include <Mod/Measure/MeasureGlobal.h>
#include <App/PropertyLinks.h>
#include <App/PropertyUnits.h>
#include <App/GeoFeature.h>
#include <Mod/Part/App/MeasureInfo.h>
#include <Mod/Part/App/TopoShape.h>
#include "MeasureBase.h"
namespace Measure
{
class MeasureExport MeasureLength : public Measure::MeasureBaseExtendable<Part::MeasureLengthInfo>
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureLength);
public:
/// Constructor
MeasureLength();
~MeasureLength() override;
App::PropertyLinkSubList Elements;
App::PropertyDistance Length;
App::DocumentObjectExecReturn *execute() override;
void recalculateLength();
const char* getViewProviderName() const override {
return "MeasureGui::ViewProviderMeasure";
}
static bool isValidSelection(const App::MeasureSelection& selection);
void parseSelection(const App::MeasureSelection& selection) override;
std::vector<std::string> getInputProps() override {return {"Elements"};}
App::Property* getResultProp() override {return &this->Length;}
// Return a placement for the viewprovider, just use the first element for now
Base::Placement getPlacement();
// Return the object we are measuring
std::vector<App::DocumentObject*> getSubject() const override;
private:
void onChanged(const App::Property* prop) override;
};
} //namespace Measure
#endif // MEASURE_MEASURELENGTH_H

View File

@@ -0,0 +1,177 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/PropertyContainer.h>
#include <App/Application.h>
#include <App/MeasureManager.h>
#include <App/Document.h>
#include "MeasurePosition.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasurePosition, Measure::MeasureBase)
MeasurePosition::MeasurePosition()
{
ADD_PROPERTY_TYPE(Element,(nullptr), "Measurement", App::Prop_None, "Element to get the position from");
Element.setScope(App::LinkScope::Global);
Element.setAllowExternal(true);
ADD_PROPERTY_TYPE(Position,(0.0, 0.0, 0.0), "Measurement", App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),
"The absolute position");
}
MeasurePosition::~MeasurePosition() = default;
bool MeasurePosition::isValidSelection(const App::MeasureSelection& selection){
if (selection.empty() || selection.size() > 1) {
return false;
}
for (auto element : selection) {
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::INVALID) {
return false;
}
if (type != App::MeasureElementType::POINT) {
return false;
}
}
return true;
}
void MeasurePosition::parseSelection(const App::MeasureSelection& selection) {
// Set properties from selection, method is only invoked when isValid Selection returns true
for (auto element : selection) {
auto objT = element.object;
std::vector<std::string> subElements {objT.getSubName()};
Element.setValue(objT.getObject(), subElements);
break;
}
}
App::DocumentObjectExecReturn *MeasurePosition::execute()
{
recalculatePosition();
return DocumentObject::StdReturn;
}
void MeasurePosition::recalculatePosition()
{
const App::DocumentObject* object = Element.getValue();
const std::vector<std::string>& subElements = Element.getSubValues();
// Get the position of the first point
std::string subElement = subElements.front();
// Get the Geometry handler based on the module
const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName();
const std::string& mod = Base::Type::getModuleName(className);
auto handler = getGeometryHandler(mod);
if (!handler) {
throw Base::RuntimeError("No geometry handler available for submitted element type");
}
App::SubObjectT subject{object, subElement.c_str()};
auto info = handler(subject);
auto positionInfo = std::dynamic_pointer_cast<Part::MeasurePositionInfo>(info);
Position.setValue(positionInfo->position);
}
void MeasurePosition::onChanged(const App::Property* prop)
{
if (isRestoring() || isRemoving()) {
return;
}
if (prop == &Element) {
recalculatePosition();
}
DocumentObject::onChanged(prop);
}
QString MeasurePosition::getResultString() {
App::Property* prop = this->getResultProp();
if (prop == nullptr) {
return {};
}
Base::Vector3d value = Position.getValue();
QString unit = Position.getUnit().getString();
int precision = 2;
QString text;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QTextStream(&text)
<< "X: " << QString::number(value.x, 'f', precision) << " " << unit << endl
<< "Y: " << QString::number(value.y, 'f', precision) << " " << unit << endl
<< "Z: " << QString::number(value.z, 'f', precision) << " " << unit;
#else
QTextStream(&text)
<< "X: " << QString::number(value.x, 'f', precision) << " " << unit << Qt::endl
<< "Y: " << QString::number(value.y, 'f', precision) << " " << unit << Qt::endl
<< "Z: " << QString::number(value.z, 'f', precision) << " " << unit;
#endif
return text;
}
Base::Placement MeasurePosition::getPlacement() {
Base::Placement placement;
placement.setPosition(Position.getValue());
return placement;
}
//! Return the object we are measuring
//! used by the viewprovider in determining visibility
std::vector<App::DocumentObject*> MeasurePosition::getSubject() const
{
return {Element.getValue()};
}

View File

@@ -0,0 +1,82 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 APP_MEASUREPOSITION_H
#define APP_MEASUREPOSITION_H
#include <Mod/Measure/MeasureGlobal.h>
#include <QTextStream>
#include <App/DocumentObject.h>
#include <App/PropertyGeo.h>
#include <App/PropertyUnits.h>
#include <Mod/Part/App/MeasureInfo.h>
#include "MeasureBase.h"
namespace Measure
{
class MeasureExport MeasurePosition : public Measure::MeasureBaseExtendable<Part::MeasurePositionInfo>
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasurePosition);
public:
/// Constructor
MeasurePosition();
~MeasurePosition() override;
App::PropertyLinkSub Element;
App::PropertyPosition Position;
App::DocumentObjectExecReturn *execute() override;
void recalculatePosition();
const char* getViewProviderName() const override {
return "MeasureGui::ViewProviderMeasure";
}
static bool isValidSelection(const App::MeasureSelection& selection);
void parseSelection(const App::MeasureSelection& selection) override;
std::vector<std::string> getInputProps() override {return {"Element"};}
App::Property* getResultProp() override {return &this->Position;}
QString getResultString() override;
Base::Placement getPlacement() override;
// Return the object we are measuring
std::vector<App::DocumentObject*> getSubject() const override;
private:
void onChanged(const App::Property* prop) override;
};
} //namespace Measure
#endif // APP_MEASUREPOSITION_H

View File

@@ -0,0 +1,211 @@
/***************************************************************************
* Copyright (c) 2023 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"
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Wire.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepLProp_CLProps.hxx>
#include <App/Application.h>
#include <App/Document.h>
#include <App/MeasureManager.h>
#include <Mod/Part/App/PartFeature.h>
#include "MeasureRadius.h"
using namespace Measure;
PROPERTY_SOURCE(Measure::MeasureRadius, Measure::MeasureBase)
MeasureRadius::MeasureRadius()
{
ADD_PROPERTY_TYPE(Element,(nullptr), "Measurement", App::Prop_None, "Element to get the radius from");
Element.setScope(App::LinkScope::Global);
Element.setAllowExternal(true);
ADD_PROPERTY_TYPE(Radius,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),
"Radius of selection");
}
MeasureRadius::~MeasureRadius() = default;
//! validate all the object+subelement pairs in the selection. Must be circle or arc
//! and have a geometry handler available. We only calculate radius if there is a
//! single valid item in the selection
bool MeasureRadius::isValidSelection(const App::MeasureSelection& selection){
if (selection.empty() || selection.size() > 1) {
// too few or too many selections
return false;
}
auto element = selection.front();
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::INVALID) {
return false;
}
if (type != App::MeasureElementType::CIRCLE
&& type != App::MeasureElementType::ARC) {
return false;
}
return true;
}
//! return true if the selection is particularly interesting to MeasureRadius.
//! In this case we claim circles and arcs.
bool MeasureRadius::isPrioritizedSelection(const App::MeasureSelection& selection) {
if (selection.size() != 1) {
return false;
}
auto element = selection.front();
auto objT = element.object;
App::DocumentObject* ob = objT.getObject();
const std::string& subName = objT.getSubName();
const char* className = objT.getSubObject()->getTypeId().getName();
std::string mod = Base::Type::getModuleName(className);
if (!hasGeometryHandler(mod)) {
return false;
}
App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str());
App::MeasureElementType type = handler.typeCb(ob, subName.c_str());
if (type == App::MeasureElementType::CIRCLE
|| type == App::MeasureElementType::ARC) {
return true;
}
return false;
}
//! Set properties from first item in selection. assumes a valid selection.
void MeasureRadius::parseSelection(const App::MeasureSelection& selection) {
auto element = selection.front();
auto objT = element.object;
std::vector<std::string> subElementList { objT.getSubName() };
Element.setValue(objT.getObject(), subElementList);
}
App::DocumentObjectExecReturn *MeasureRadius::execute()
{
recalculateRadius();
return DocumentObject::StdReturn;
}
void MeasureRadius::recalculateRadius()
{
Radius.setValue(getMeasureInfoFirst()->radius);
}
void MeasureRadius::onChanged(const App::Property* prop)
{
if (isRestoring() || isRemoving()) {
return;
}
if (prop == &Element) {
recalculateRadius();
}
MeasureBase::onChanged(prop);
}
//! return a placement (location + orientation) for the first element
Base::Placement MeasureRadius::getPlacement() {
auto loc = getMeasureInfoFirst()->pointOnCurve;
auto p = Base::Placement();
p.setPosition(loc);
return p;
}
//! return the pointOnCurve element in MeasureRadiusInfo for the first element
Base::Vector3d MeasureRadius::getPointOnCurve() const
{
return getMeasureInfoFirst()->pointOnCurve;
}
//! get the handler's result for the first element
Part::MeasureRadiusInfoPtr MeasureRadius::getMeasureInfoFirst() const
{
const App::DocumentObject* object = Element.getValue();
const std::vector<std::string>& subElements = Element.getSubValues();
if (!object || subElements.empty()) {
// NOLINTNEXTLINE(modernize-return-braced-init-list)
return std::make_shared<Part::MeasureRadiusInfo>();
}
std::string subElement = subElements.front();
const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName();
const std::string& mod = Base::Type::getModuleName(className);
auto handler = getGeometryHandler(mod);
if (!handler) {
throw Base::RuntimeError("No geometry handler available for submitted element type");
}
std::string obName = object->getNameInDocument();
App::SubObjectT subject{object, subElement.c_str()};
auto info = handler(subject);
auto radiusInfo = std::dynamic_pointer_cast<Part::MeasureRadiusInfo>(info);
return radiusInfo;
}
//! Return the object we are measuring
//! used by the viewprovider in determining visibility
std::vector<App::DocumentObject*> MeasureRadius::getSubject() const
{
return {Element.getValue()};
}

View File

@@ -0,0 +1,90 @@
/***************************************************************************
* Copyright (c) 2023 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_MEASURERADIUS_H
#define MEASURE_MEASURERADIUS_H
#include <Mod/Measure/MeasureGlobal.h>
#include <App/Application.h>
#include <App/PropertyLinks.h>
#include <App/PropertyUnits.h>
#include <App/GeoFeature.h>
#include <Base/Placement.h>
#include <Mod/Part/App/MeasureInfo.h>
#include "MeasureBase.h"
namespace Measure
{
class MeasureExport MeasureRadius : public Measure::MeasureBaseExtendable<Part::MeasureRadiusInfo>
{
PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureRadius);
public:
/// Constructor
MeasureRadius();
~MeasureRadius() override;
App::PropertyLinkSub Element;
App::PropertyDistance Radius;
App::DocumentObjectExecReturn *execute() override;
const char* getViewProviderName() const override {
return "MeasureGui::ViewProviderMeasure";
}
void recalculateRadius();
static bool isValidSelection(const App::MeasureSelection& selection);
static bool isPrioritizedSelection(const App::MeasureSelection& selection);
void parseSelection(const App::MeasureSelection& selection) override;
std::vector<std::string> getInputProps() override {return {"Element"};}
App::Property* getResultProp() override {return &this->Radius;}
// Return a placement for the viewprovider, just use the first element for now
Base::Placement getPlacement() override;
// Return a point on curve for the viewprovider
Base::Vector3d getPointOnCurve() const;
// Return the object we are measuring
std::vector<App::DocumentObject*> getSubject() const override;
private:
void onChanged(const App::Property* prop) override;
Part::MeasureRadiusInfoPtr getMeasureInfoFirst() const;
};
} //namespace Measure
#endif // MEASURE_MEASURERADIUS_H

View File

@@ -202,7 +202,7 @@ TopoDS_Shape Measurement::getShape(App::DocumentObject *obj , const char *subNam
try {
Part::TopoShape partShape = Part::Feature::getTopoShape(obj);
App::GeoFeature* geoFeat = dynamic_cast<App::GeoFeature*>(obj);
App::GeoFeature* geoFeat = dynamic_cast<App::GeoFeature*>(obj);
if (geoFeat) {
partShape.setPlacement(geoFeat->globalPlacement());
}

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- this creates the bindings for the original measurement objects & methods. it is not part of unified measurement facility -->
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"

View File

@@ -0,0 +1,80 @@
/***************************************************************************
* Copyright (c) 2023 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_
# include <string>
#endif
#include <App/Application.h>
#include <App/Material.h>
#include <Base/Console.h>
#include <Base/Parameter.h>
#include "Preferences.h"
//getters for parameters used in multiple places.
//ensure this is in sync with parameter names and default values on preference pages
using namespace Measure;
//! Returns the Measure preference group
Base::Reference<ParameterGrp> Preferences::getPreferenceGroup(const char* Name)
{
return App::GetApplication().GetUserParameter().GetGroup("BaseApp/Preferences/Mod/Measure")->GetGroup(Name);
}
App::Color Preferences::defaultLineColor()
{
App::Color fcColor;
fcColor.setPackedValue(getPreferenceGroup("Appearance")->GetUnsigned("DefaultLineColor", 0xFFFFFFFF));
return fcColor;
}
App::Color Preferences::defaultTextColor()
{
App::Color fcColor;
fcColor.setPackedValue(getPreferenceGroup("Appearance")->GetUnsigned("DefaultTextColor", 0x00000000));
return fcColor;
}
App::Color Preferences::defaultTextBackgroundColor()
{
App::Color fcColor;
fcColor.setPackedValue(getPreferenceGroup("Appearance")->GetUnsigned("DefaultTextBackgroundColor", 0xFFFFFFFF));
return fcColor;
}
double Preferences::defaultDistFactor()
{
return getPreferenceGroup("Appearance")->GetFloat("DefaultDistFactor", 1.0);
}
int Preferences::defaultFontSize()
{
return getPreferenceGroup("Appearance")->GetInt("DefaultFontSize", 18);
}
bool Preferences::defaultMirror()
{
return getPreferenceGroup("Appearance")->GetBool("DefaultMirror", false);
}

View File

@@ -0,0 +1,58 @@
/***************************************************************************
* Copyright (c) 2023 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 Preferences_h_
#define Preferences_h_
#include <Mod/Measure/MeasureGlobal.h>
#include <string>
#include <Base/Parameter.h>
#include <App/Material.h>
namespace App
{
class Color;
}
namespace Measure
{
//getters for parameters used in multiple places.
class MeasureExport Preferences
{
public:
static Base::Reference<ParameterGrp> getPreferenceGroup(const char* Name);
static App::Color defaultLineColor();
static App::Color defaultTextColor();
static double defaultDistFactor();
static int defaultFontSize();
static bool defaultMirror();
static App::Color defaultTextBackgroundColor();
};
}//end namespace Measure
#endif

View File

@@ -1,12 +1,29 @@
add_subdirectory(App)
if(FREECAD_BUILD_GUI)
#add_subdirectory(Gui)
endif(FREECAD_BUILD_GUI)
set(Measure_Scripts
Init.py
UtilsMeasure.py
MeasureCOM.py
)
if(BUILD_GUI)
list(APPEND Measure_Scripts InitGui.py)
add_subdirectory(Gui)
endif(BUILD_GUI)
add_custom_target(MeasureScripts ALL
SOURCES ${Measure_Scripts}
)
fc_target_copy_resource(MeasureScripts
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/Mod/Measure
${Measure_Scripts})
INSTALL(
FILES
Init.py
${Measure_Scripts}
DESTINATION
Mod/Measure
)

View File

@@ -0,0 +1,99 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/PyObjectBase.h>
#include <Base/Tools.h>
#include <Gui/Application.h>
#include <Gui/Language/Translator.h>
#include <Gui/WidgetFactory.h>
#include "DlgPrefsMeasureAppearanceImp.h"
#include "ViewProviderMeasureAngle.h"
#include "ViewProviderMeasureDistance.h"
#include "ViewProviderMeasureBase.h"
// use a different name to CreateCommand()
void CreateMeasureCommands();
namespace MeasureGui
{
class Module: public Py::ExtensionModule<Module>
{
public:
Module()
: Py::ExtensionModule<Module>("MeasureGui")
{
initialize("This module is the MeasureGui module.");// register with Python
}
~Module() override = default;
private:
};
PyObject* initModule()
{
return Base::Interpreter().addModule(new Module);
}
}// namespace MeasureGui
/* Python entry */
PyMOD_INIT_FUNC(MeasureGui)
{
if (!Gui::Application::Instance) {
PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application.");
PyMOD_Return(nullptr);
}
// load dependent module
try {
Base::Interpreter().loadModule("Measure");
}
catch(const Base::Exception& e) {
PyErr_SetString(PyExc_ImportError, e.what());
PyMOD_Return(nullptr);
}
PyObject* mod = MeasureGui::initModule();
Base::Console().Log("Loading GUI of Measure module... done\n");
// instantiating the commands
CreateMeasureCommands();
MeasureGui::ViewProviderMeasureBase ::init();
MeasureGui::ViewProviderMeasure ::init();
MeasureGui::ViewProviderMeasureAngle ::init();
MeasureGui::ViewProviderMeasureDistance ::init();
// register preferences pages
new Gui::PrefPageProducer<MeasureGui::DlgPrefsMeasureAppearanceImp>(QT_TRANSLATE_NOOP("QObject", "Measure"));
// Q_INIT_RESOURCE(Measure);
PyMOD_Return(mod);
}

View File

@@ -0,0 +1,90 @@
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
${Boost_INCLUDE_DIRS}
${COIN3D_INCLUDE_DIRS}
${OCC_INCLUDE_DIR}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
)
link_directories(${OCC_LIBRARY_DIR})
link_directories(${OCC_LIBRARY_DIR})
set(MeasureGui_LIBS
Measure
FreeCADGui
)
if(MSVC)
include_directories(
${CMAKE_SOURCE_DIR}/src/3rdParty/OpenGL/api
)
endif(MSVC)
set (Measure_TR_QRC ${CMAKE_CURRENT_BINARY_DIR}/Resources/Measure_translation.qrc)
qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts"
${CMAKE_CURRENT_BINARY_DIR}/Resources/translations)
qt_create_resource_file(${Measure_TR_QRC} ${QM_SRCS})
qt_add_resources(MeasureGui_SRCS Resources/Measure.qrc ${Measure_TR_QRC})
SET(MeasureGui_UIC_SRCS
DlgPrefsMeasureAppearanceImp.ui
)
SET(MeasureGui_SRCS
${CMAKE_SOURCE_DIR}/src/Mod/Measure/InitGui.py
${MeasureGui_SRCS}
AppMeasureGui.cpp
Command.cpp
Resources/Measure.qrc
PreCompiled.cpp
PreCompiled.h
ViewProviderMeasureBase.cpp
ViewProviderMeasureBase.h
ViewProviderMeasureAngle.cpp
ViewProviderMeasureAngle.h
ViewProviderMeasureDistance.cpp
ViewProviderMeasureDistance.h
# Workbench.cpp
# Workbench.h
DlgPrefsMeasureAppearanceImp.ui
DlgPrefsMeasureAppearanceImp.cpp
DlgPrefsMeasureAppearanceImp.h
)
SET(MeasureGuiTaskDlgs_SRCS
DlgPrefsMeasureAppearanceImp.ui
)
#set(MeasureGui_Scripts
# ../InitGui.py
#)
if(FREECAD_USE_PCH)
add_definitions(-D_PreComp_)
GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${MeasureGui_SRCS})
ADD_MSVC_PRECOMPILED_HEADER(MeasureGui PreCompiled.h PreCompiled.cpp PCH_SRCS)
endif(FREECAD_USE_PCH)
SET(MeasureGuiIcon_SVG
Resources/icons/preferences-measure.svg
)
add_library(MeasureGui SHARED ${MeasureGui_SRCS} ${MeasureGuiIcon_SVG})
target_link_libraries(MeasureGui ${MeasureGui_LIBS})
SET_BIN_DIR(MeasureGui MeasureGui /Mod/Measure)
SET_PYTHON_PREFIX_SUFFIX(MeasureGui)
fc_copy_sources(MeasureGui "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Measure/" ${MeasureGuiIcon_SVG})
INSTALL(TARGETS MeasureGui DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(FILES ${MeasureGuiIcon_SVG} DESTINATION "${CMAKE_INSTALL_DATADIR}/Mod/Measure/Resources/icons")

View File

@@ -0,0 +1,33 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <Gui/Application.h>
#include <Gui/Command.h>
using namespace std;
void CreateMeasureCommands() {
Base::Console().Message("Init MeasureGui\n");
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
}

View File

@@ -0,0 +1,75 @@
/**************************************************************************
* Copyright (c) 2023 Wanderer Fan <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"
#include "DlgPrefsMeasureAppearanceImp.h"
#include "ui_DlgPrefsMeasureAppearanceImp.h"
using namespace MeasureGui;
DlgPrefsMeasureAppearanceImp::DlgPrefsMeasureAppearanceImp( QWidget* parent )
: PreferencePage( parent )
, ui(new Ui_DlgPrefsMeasureAppearanceImp)
{
ui->setupUi(this);
}
DlgPrefsMeasureAppearanceImp::~DlgPrefsMeasureAppearanceImp()
{
// no need to delete child widgets, Qt does it all for us
}
void DlgPrefsMeasureAppearanceImp::saveSettings()
{
ui->cbMirror->onSave();
ui->dsbDistFactor->onSave();
ui->sbFontSize->onSave();
ui->cbText->onSave();
ui->cbLine->onSave();
ui->cbBackground->onSave();
}
void DlgPrefsMeasureAppearanceImp::loadSettings()
{
ui->sbFontSize->onRestore();
ui->cbText->onRestore();
ui->cbBackground->onRestore();
ui->cbLine->onRestore();
ui->cbMirror->onRestore();
ui->dsbDistFactor->onRestore();
}
/**
* Sets the strings of the subwidgets using the current language.
*/
void DlgPrefsMeasureAppearanceImp::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
}
else {
QWidget::changeEvent(e);
}
}
#include <Mod/Measure/Gui/moc_DlgPrefsMeasureAppearanceImp.cpp>

View File

@@ -0,0 +1,55 @@
/**************************************************************************
* Copyright (c) 2023 Wanderer Fan <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 MeasureGui_DlgPrefsAppearanceImp_H
#define MeasureGui_DlgPrefsAppearanceImp_H
#include <memory>
#include <Gui/PropertyPage.h>
#include <Mod/Measure/MeasureGlobal.h>
namespace MeasureGui {
class Ui_DlgPrefsMeasureAppearanceImp;
class DlgPrefsMeasureAppearanceImp : public Gui::Dialog::PreferencePage
{
Q_OBJECT
public:
explicit DlgPrefsMeasureAppearanceImp( QWidget* parent = nullptr );
~DlgPrefsMeasureAppearanceImp() override;
protected:
void saveSettings() override;
void loadSettings() override;
void changeEvent(QEvent *e) override;
private:
std::unique_ptr<Ui_DlgPrefsMeasureAppearanceImp> ui;
};
} // namespace MeasureGui
#endif // MeasureGui_DlgPrefsAppearanceImp_H

View File

@@ -0,0 +1,247 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MeasureGui::DlgPrefsMeasureAppearanceImp</class>
<widget class="QWidget" name="MeasureGui::DlgPrefsMeasureAppearanceImp">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>623</width>
<height>287</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Appearance</string>
</property>
<property name="windowIcon">
<iconset resource="Resources/Measure.qrc">
<normaloff>:/icons/preferences-measure.svg</normaloff>:/icons/preferences-measure.svg</iconset>
</property>
<property name="toolTip">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="gbMisc">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>200</height>
</size>
</property>
<property name="title">
<string>Default Property Values</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_5" columnstretch="1,1">
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Distance Factor</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Text color</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::PrefColorButton" name="cbText">
<property name="color">
<color>
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultTextColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Measure/Appearance</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::PrefSpinBox" name="sbFontSize">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="value">
<number>18</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultFontSize</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Measure/Appearance</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::PrefColorButton" name="cbLine">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="color">
<color>
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultLineColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Measure/Appearance</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Text size</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::PrefDoubleSpinBox" name="dsbDistFactor">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultDistFactor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Measure/Appearance</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Line color</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="Gui::PrefCheckBox" name="cbMirror">
<property name="text">
<string>Mirror</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultMirror</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Measure/Appearance</cstring>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Background color</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::PrefColorButton" name="cbBackground">
<property name="color">
<color>
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>DefaultTextBackgroundColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Measure/Appearance</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::ColorButton</class>
<extends>QPushButton</extends>
<header>Gui/Widgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefSpinBox</class>
<extends>QSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefColorButton</class>
<extends>Gui::ColorButton</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="Resources/Measure.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -0,0 +1,22 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"

View File

@@ -0,0 +1,97 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 MEASUREGUI_PRECOMPILED_H
#define MEASUREGUI_PRECOMPILED_H
#include <FCConfig.h>
#include <Mod/Measure/MeasureGlobal.h>
// point at which warnings of overly long specifiers disabled (needed for VC6)
#ifdef _MSC_VER
# pragma warning( disable : 4251 )
# pragma warning( disable : 4503 )
# pragma warning( disable : 4786 ) // specifier longer then 255 chars
# pragma warning( disable : 4273 )
#endif
#ifdef FC_OS_WIN32
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
#endif
#ifdef _PreComp_
// standard
#include <cfloat>
#include <cmath>
// STL
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <vector>
// OpenCasCade
#include <Mod/Part/App/OpenCascadeAll.h>
// Boost
#include <boost/regex.hpp>
#include <boost/algorithm/string/predicate.hpp>
// GL
// Include glext before QtAll/InventorAll
#ifdef FC_OS_WIN32
# include <GL/gl.h>
# include <GL/glext.h>
#else
# ifdef FC_OS_MACOSX
# include <OpenGL/gl.h>
# include <OpenGL/glext.h>
# else
# ifndef GL_GLEXT_PROTOTYPES
# define GL_GLEXT_PROTOTYPES 1
# endif
# include <GL/gl.h>
# include <GL/glext.h>
# endif //FC_OS_MACOSX
#endif //FC_OS_WIN32
// Should come after glext.h to avoid warnings
#include <Inventor/C/glue/gl.h>
// Qt Toolkit
#ifndef __QtAll__
# include <Gui/QtAll.h>
#endif
// Inventor includes OpenGL
#ifndef __InventorAll__
# include <Gui/InventorAll.h>
#endif
#endif //_PreComp_
#endif

View File

@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>icons/umf-measurement.svg</file>
<file>icons/preferences-measure.svg</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
id="svg2869"
version="1.1"
viewBox="0 0 64 64"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2871">
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop10" />
<stop
style="stop-color:#888a85;stop-opacity:1"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop19" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop20" />
</linearGradient>
<linearGradient
id="swatch18">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop18" />
</linearGradient>
<linearGradient
id="swatch15">
<stop
style="stop-color:#3d0000;stop-opacity:1;"
offset="0"
id="stop15" />
</linearGradient>
<linearGradient
id="linearGradient5-1">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
id="linearGradient3836-9">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11"
x1="12.375"
y1="23.75"
x2="64"
y2="28.5"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata2874">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[maxwxyz]</dc:title>
</cc:Agent>
</dc:creator>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:date>2024</dc:date>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer3"
style="display:inline">
<path
id="path4498"
d="M 31.906888,60.999998 V 12.276532 l -24.5860006,1e-6 -2.904e-4,-3.0939183 24.58542,-6.1826135 h 8.71e-4 15.462656 v 9.2765308 h 13.308118 l 2.89e-4,2.030582 -13.308407,4.061163 v 42.631722 z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path6"
d="m 32.154297,5 h 13.214844 v 9.277344 h 8.55664 l -8.55664,2.611328 V 59 H 33.90625 V 10.277344 H 11.164063 Z"
style="fill:url(#linearGradient11);fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4504"
d="m 33.453879,17.976538 9.277594,8.71e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1"
d="m 33.457361,48.841436 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path2"
d="m 33.457361,41.125284 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3"
d="m 33.457361,33.409133 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4"
d="m 33.457361,25.692981 6.185063,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5"
d="m 33.453879,56.557588 9.277594,8.72e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path11"
d="m 34.230469,19.976538 7.724414,8.71e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path12"
d="m 34.233368,50.841436 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path13"
d="m 34.233368,43.125284 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path14"
d="m 34.233368,35.409133 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path15"
d="m 34.233368,27.692981 5.14961,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path16"
d="m 34.230469,58.557588 8.501004,8.72e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4500"
d="M 7.3208874,27.158429 H 34.22781 l 0.0019,28.741042 H 28.043227 V 37.209528 L 7.3208874,32.571857 Z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4502"
d="m 45.82444,20.585383 14.854673,4.062178 2.9e-4,2.510868 H 50.463816 l 0.0016,14.741042 h -4.638797 z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path7"
d="M 9.3203125,29.158203 H 32.228516 l 0.002,24.742188 h -2.1875 V 35.607422 L 9.3203125,30.970703 Z"
style="fill:#888a85;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path18"
d="m 31.176195,46.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path19"
d="m 31.176195,48.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path20"
d="m 31.176195,42.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path21"
d="m 31.176195,44.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path22"
d="m 31.176195,38.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path23"
d="m 31.176195,40.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path24"
d="m 31.176195,50.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path25"
d="m 31.176195,52.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path26"
d="m 31.176195,34.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path27"
d="m 31.176195,36.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
id="svg2869"
version="1.1"
viewBox="0 0 64 64"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2871">
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop10" />
<stop
style="stop-color:#888a85;stop-opacity:1"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop19" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop20" />
</linearGradient>
<linearGradient
id="swatch18">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop18" />
</linearGradient>
<linearGradient
id="swatch15">
<stop
style="stop-color:#3d0000;stop-opacity:1;"
offset="0"
id="stop15" />
</linearGradient>
<linearGradient
id="linearGradient5-1">
<stop
style="stop-color:#ef2929;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#ef2929;stop-opacity:0;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
id="linearGradient3836-9">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11"
x1="12.375"
y1="23.75"
x2="64"
y2="28.5"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata2874">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[maxwxyz]</dc:title>
</cc:Agent>
</dc:creator>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:date>2024</dc:date>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer3"
style="display:inline">
<path
id="path4498"
d="M 31.906888,60.999998 V 12.276532 l -24.5860006,1e-6 -2.904e-4,-3.0939183 24.58542,-6.1826135 h 8.71e-4 15.462656 v 9.2765308 h 13.308118 l 2.89e-4,2.030582 -13.308407,4.061163 v 42.631722 z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path6"
d="m 32.154297,5 h 13.214844 v 9.277344 h 8.55664 l -8.55664,2.611328 V 59 H 33.90625 V 10.277344 H 11.164063 Z"
style="fill:url(#linearGradient11);fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4504"
d="m 33.453879,17.976538 9.277594,8.71e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1"
d="m 33.457361,48.841436 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path2"
d="m 33.457361,41.125284 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path3"
d="m 33.457361,33.409133 6.185063,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4"
d="m 33.457361,25.692981 6.185063,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path5"
d="m 33.453879,56.557588 9.277594,8.72e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path11"
d="m 34.230469,19.976538 7.724414,8.71e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path12"
d="m 34.233368,50.841436 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path13"
d="m 34.233368,43.125284 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path14"
d="m 34.233368,35.409133 5.14961,5.81e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path15"
d="m 34.233368,27.692981 5.14961,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path16"
d="m 34.230469,58.557588 8.501004,8.72e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4500"
d="M 7.3208874,27.158429 H 34.22781 l 0.0019,28.741042 H 28.043227 V 37.209528 L 7.3208874,32.571857 Z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4502"
d="m 45.82444,20.585383 14.854673,4.062178 2.9e-4,2.510868 H 50.463816 l 0.0016,14.741042 h -4.638797 z"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path7"
d="M 9.3203125,29.158203 H 32.228516 l 0.002,24.742188 h -2.1875 V 35.607422 L 9.3203125,30.970703 Z"
style="fill:#888a85;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path18"
d="m 31.176195,46.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path19"
d="m 31.176195,48.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path20"
d="m 31.176195,42.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path21"
d="m 31.176195,44.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path22"
d="m 31.176195,38.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path23"
d="m 31.176195,40.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path24"
d="m 31.176195,50.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path25"
d="m 31.176195,52.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path26"
d="m 31.176195,34.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#0b1e23;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path27"
d="m 31.176195,36.622396 1.64761,5.8e-4"
style="fill:#d3d7cf;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>MeasureGui::DlgPrefsMeasureAppearanceImp</name>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="20"/>
<source>Appearance</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="47"/>
<source>Default Property Values</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="55"/>
<source>Distance Factor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="62"/>
<source>Text color</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="128"/>
<source>Text size</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="151"/>
<source>Line color</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="DlgPrefsMeasureAppearanceImp.ui" line="158"/>
<source>Mirror</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@@ -0,0 +1,339 @@
/***************************************************************************
* 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 "PreCompiled.h"
#ifndef _PreComp_
# 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>
#endif
#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 <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: couldn't 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("couldn't 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 = "umf-measurement";
// 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();
constexpr double radiansPerDegree = M_PI / 180.0;
this->fieldAngle = angleDeg * radiansPerDegree;
// 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, 10, 0));
}

View File

@@ -0,0 +1,73 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 GUI_VIEWPROVIDERMEASUREANGLE_H
#define GUI_VIEWPROVIDERMEASUREANGLE_H
#include <Mod/Measure/MeasureGlobal.h>
#include <QObject>
#include <Inventor/fields/SoSFFloat.h>
#include <Inventor/fields/SoSFMatrix.h>
#include <Inventor/fields/SoSFVec3f.h>
#include <Mod/Measure/App/MeasureAngle.h>
#include "ViewProviderMeasureBase.h"
//NOLINTBEGIN
class SoText2;
class SoTranslation;
class SoCoordinate3;
class SoIndexedLineSet;
class SoTransform;
//NOLINTEND
namespace MeasureGui
{
class MeasureGuiExport ViewProviderMeasureAngle : public MeasureGui::ViewProviderMeasureBase
{
PROPERTY_HEADER_WITH_OVERRIDE(MeasureGui::ViewProviderMeasureAngle);
public:
/// Constructor
ViewProviderMeasureAngle();
Measure::MeasureAngle* getMeasureAngle();
void redrawAnnotation() override;
void positionAnno(const Measure::MeasureBase* measureObject) override;
private:
// Fields
SoSFFloat fieldAngle; //radians.
SbMatrix getMatrix();
};
} //namespace MeasureGui
#endif // GUI_VIEWPROVIDERMEASUREANGLE_H

View File

@@ -0,0 +1,597 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 "Gui/Application.h"
#include "Gui/MDIView.h"
#include "PreCompiled.h"
#ifndef _PreComp_
# include <Inventor/actions/SoGetMatrixAction.h>
# include <Inventor/nodes/SoAnnotation.h>
# include <Inventor/nodes/SoBaseColor.h>
# include <Inventor/nodes/SoCoordinate3.h>
# include <Inventor/nodes/SoCamera.h>
# include <Inventor/nodes/SoDrawStyle.h>
# include <Inventor/nodes/SoIndexedLineSet.h>
# include <Inventor/nodes/SoMarkerSet.h>
# include <Inventor/nodes/SoPickStyle.h>
# include <Inventor/draggers/SoTranslate2Dragger.h>
# include <Inventor/engines/SoComposeMatrix.h>
# include <Inventor/engines/SoTransformVec3f.h>
# include <Inventor/engines/SoConcatenate.h>
#endif
#include <App/DocumentObject.h>
#include <Base/Console.h>
#include <Gui/Document.h>
#include <Gui/ViewParams.h>
#include <Gui/Inventor/MarkerBitmaps.h>
#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include <Mod/Measure/App/Preferences.h>
#include "ViewProviderMeasureBase.h"
using namespace MeasureGui;
using namespace Measure;
//NOLINTBEGIN
PROPERTY_SOURCE(MeasureGui::ViewProviderMeasureBase, Gui::ViewProviderDocumentObject)
//NOLINTEND
ViewProviderMeasureBase::ViewProviderMeasureBase()
{
static const char *agroup = "Appearance";
//NOLINTBEGIN
ADD_PROPERTY_TYPE(TextColor, (Preferences::defaultTextColor()), agroup, App::Prop_None, "Color for the measurement text");
ADD_PROPERTY_TYPE(TextBackgroundColor, (Preferences::defaultTextBackgroundColor()), agroup, App::Prop_None, "Color for the measurement text background");
ADD_PROPERTY_TYPE(LineColor, (Preferences::defaultLineColor()), agroup, App::Prop_None, "Color for the measurement lines");
ADD_PROPERTY_TYPE(FontSize, (Preferences::defaultFontSize()), agroup, App::Prop_None, "Size of measurement text");
//NOLINTEND
// setupAnnoSceneGraph() - sets up the annotation scene graph
pLabel = new Gui::SoFrameLabel();
pLabel->ref();
pColor = new SoBaseColor();
pColor->ref();
pLabelTranslation = new SoTransform();
pLabelTranslation->ref();
auto ps = getSoPickStyle();
// Dragger
SoSeparator* dragSeparator = new SoSeparator();
pDragger = new SoTranslate2Dragger();
pDragger->ref();
pDraggerOrientation = new SoTransform();
pDraggerOrientation->ref();
dragSeparator->addChild(pDraggerOrientation);
dragSeparator->addChild(pDragger);
// Transform drag location by dragger local orientation and connect to labelTranslation
auto matrixEngine = new SoComposeMatrix();
matrixEngine->rotation.connectFrom(&pDraggerOrientation->rotation);
auto transformEngine = new SoTransformVec3f();
transformEngine->vector.connectFrom(&pDragger->translation);
transformEngine->matrix.connectFrom(&matrixEngine->matrix);
pLabelTranslation->translation.connectFrom(&transformEngine->point);
pTextSeparator = new SoSeparator();
pTextSeparator->ref();
pTextSeparator->addChild(dragSeparator);
pTextSeparator->addChild(pLabelTranslation);
pTextSeparator->addChild(pLabel);
// Empty line separator which can be populated by inherited class
pLineSeparator = new SoSeparator();
pLineSeparator->ref();
pLineSeparator->addChild(ps);
pLineSeparator->addChild(getSoLineStylePrimary());
pLineSeparator->addChild(pColor);
// Secondary line separator
pLineSeparatorSecondary = new SoSeparator();
pLineSeparatorSecondary->ref();
pLineSeparatorSecondary->addChild(ps);
pLineSeparatorSecondary->addChild(getSoLineStyleSecondary());
pLineSeparatorSecondary->addChild(pColor);
pRootSeparator = new SoAnnotation();
pRootSeparator->ref();
pRootSeparator->addChild(pLineSeparator);
pRootSeparator->addChild(pLineSeparatorSecondary);
pRootSeparator->addChild(pTextSeparator);
addDisplayMaskMode(pRootSeparator, "Base");
pRootSeparator->touch();
pTextSeparator->touch();
pLineSeparator->touch();
// Register dragger callback
auto dragger = pDragger;
dragger->addValueChangedCallback(draggerChangedCallback, this);
// Use the label node as the transform handle
SoSearchAction sa;
sa.setInterest(SoSearchAction::FIRST);
sa.setSearchingAll(true);
sa.setNode(pLabel);
sa.apply(pcRoot);
SoPath * labelPath = sa.getPath();
assert(labelPath);
dragger->setPartAsPath("translator", labelPath);
// Hide the dragger feedback during translation
dragger->setPart("translatorActive", NULL);
dragger->setPart("xAxisFeedback", NULL);
dragger->setPart("yAxisFeedback", NULL);
// end setupSceneGraph
// these touches cause onChanged to run which then updates pLabel and pColor with the initial values
TextColor.touch();
TextBackgroundColor.touch();
FontSize.touch();
LineColor.touch();
}
ViewProviderMeasureBase::~ViewProviderMeasureBase()
{
_mVisibilityChangedConnection.disconnect();
pLabel->unref();
pColor->unref();
pDragger->unref();
pDraggerOrientation->unref();
pLabelTranslation->unref();
pTextSeparator->unref();
pLineSeparator->unref();
pRootSeparator->unref();
}
std::vector<std::string> ViewProviderMeasureBase::getDisplayModes() const
{
// add modes
std::vector<std::string> StrList;
StrList.emplace_back("Base");
return StrList;
}
void ViewProviderMeasureBase::setDisplayMode(const char* ModeName)
{
if (strcmp(ModeName, "Base") == 0) {
setDisplayMaskMode("Base");
}
ViewProviderDocumentObject::setDisplayMode(ModeName);
}
void ViewProviderMeasureBase::finishRestoring() {
// Force measurement visibility when loading a document
show();
}
void ViewProviderMeasureBase::onChanged(const App::Property* prop)
{
if (prop == &TextColor) {
const App::Color& color = TextColor.getValue();
pLabel->textColor.setValue(color.r, color.g, color.b);
}
else if (prop == &TextBackgroundColor) {
const App::Color& color = TextBackgroundColor.getValue();
pLabel->backgroundColor.setValue(color.r, color.g, color.b);
}
else if (prop == &LineColor) {
const App::Color& color = LineColor.getValue();
pColor->rgb.setValue(color.r, color.g, color.b);
}
else if (prop == &FontSize) {
pLabel->size = FontSize.getValue();
}
ViewProviderDocumentObject::onChanged(prop);
}
void ViewProviderMeasureBase::draggerChangedCallback(void *data, SoDragger *) {
auto me = static_cast<ViewProviderMeasureBase*>(data);
me->onLabelMoved();
}
void ViewProviderMeasureBase::setLabelValue(const Base::Quantity& value) {
pLabel->string.setValue(value.getUserString().toUtf8().constData());
}
void ViewProviderMeasureBase::setLabelValue(const QString& value) {
auto lines = value.split(QString::fromLatin1("\n"));
int i = 0;
for (auto& it : lines) {
pLabel->string.set1Value(i, it.toUtf8().constData());
i++;
}
}
void ViewProviderMeasureBase::setLabelTranslation(const SbVec3f& position) {
// Set the dragger translation to keep it in sync with pLabelTranslation
pDragger->translation.setValue(position);
}
SoPickStyle* ViewProviderMeasureBase::getSoPickStyle() {
auto ps = new SoPickStyle();
ps->style = SoPickStyle::UNPICKABLE;
return ps;
}
SoDrawStyle* ViewProviderMeasureBase::getSoLineStylePrimary() {
auto style = new SoDrawStyle();
style->lineWidth = 2.0f;
return style;
}
SoDrawStyle* ViewProviderMeasureBase::getSoLineStyleSecondary() {
auto style = new SoDrawStyle();
style->lineWidth = 1.0f;
return style;
}
SoSeparator* ViewProviderMeasureBase::getSoSeparatorText() {
return pTextSeparator;
}
void ViewProviderMeasureBase::positionAnno(const Measure::MeasureBase* measureObject) {
(void)measureObject;
}
void ViewProviderMeasureBase::attach(App::DocumentObject *pcObj)
{
ViewProviderDocumentObject::attach(pcObj);
positionAnno(static_cast<MeasureBase*>(pcObj));
}
//! handle changes to the feature's properties
void ViewProviderMeasureBase::updateData(const App::Property* prop)
{
bool doUpdate = false;
auto obj = getMeasureObject();
if (!obj) {
return;
}
// Check if one of the input properties has been changed
auto inputProps = obj->getInputProps();
if (std::find(inputProps.begin(), inputProps.end(), std::string(prop->getName())) != inputProps.end()) {
doUpdate = true;
// Add connections to be notified when the measured objects are changed
connectToSubject(obj->getSubject());
}
// Check if the result prop has been changed
auto resultProp = obj->getResultProp();
if (resultProp && prop == resultProp){
doUpdate = true;
}
if (doUpdate) {
redrawAnnotation();
}
ViewProviderDocumentObject::updateData(prop);
}
// TODO: should this be pure virtual?
void ViewProviderMeasureBase::redrawAnnotation()
{
// Base::Console().Message("VPMB::redrawAnnotation()\n");
}
//! connect to the subject to receive visibility updates
void ViewProviderMeasureBase::connectToSubject(App::DocumentObject* subject)
{
if (!subject) {
return;
}
// disconnect any existing connection
if (_mVisibilityChangedConnection.connected()) {
_mVisibilityChangedConnection.disconnect();
}
//NOLINTBEGIN
auto bndVisibility = std::bind(&ViewProviderMeasureBase::onSubjectVisibilityChanged, this, std::placeholders::_1, std::placeholders::_2);
//NOLINTEND
_mVisibilityChangedConnection = subject->signalChanged.connect(bndVisibility);
}
//! connect to the subject to receive visibility updates
void ViewProviderMeasureBase::connectToSubject(std::vector<App::DocumentObject*> subject)
{
if (subject.empty()) {
return;
}
// TODO: should we connect to all the subject objects when there is >1?
auto proxy = subject.front();
connectToSubject(proxy);
}
//! retrieve the feature
Measure::MeasureBase* ViewProviderMeasureBase::getMeasureObject()
{
// Note: Cast to MeasurePropertyBase once we use it to provide the needed values e.g. basePosition textPosition etc.
auto feature = dynamic_cast<Measure::MeasureBase*>(pcObject);
if (!feature) {
throw Base::RuntimeError("Feature not found for ViewProviderMeasureBase");
}
return feature;
}
//! calculate a good direction from the elements being measured to the annotation text based on the layout
//! of the elements and relationship with the cardinal axes and the view direction. elementDirection
//! is expected to be a normalized vector.
//! an example of an elementDirection would be the vector from the start of a line to the end.
Base::Vector3d ViewProviderMeasureBase::getTextDirection(Base::Vector3d elementDirection, double tolerance)
{
// TODO: this can fail if the active view is not a 3d view (spreadsheet, techdraw page) and something causes a measure to try to update
// we need to search through the mdi views for a 3d view and take the direction from it (or decide that if the active view is not 3d,
// assume we are looking from the front).
Base::Vector3d viewDirection;
Base::Vector3d upDirection;
Gui::View3DInventor* view = nullptr;
try {
view = dynamic_cast<Gui::View3DInventor*>(this->getActiveView());
} catch (const Base::RuntimeError& e) {
Base::Console().Log("ViewProviderMeasureBase::getTextDirection: Could not get active view\n");
}
if (view) {
Gui::View3DInventorViewer* viewer = view->getViewer();
viewDirection = toVector3d(viewer->getViewDirection()).Normalize();
upDirection = toVector3d(viewer->getUpDirection()).Normalize();
// Measure doesn't work with this kind of active view. Might be dependency graph, might be TechDraw, or ????
//throw Base::RuntimeError("Measure doesn't work with this kind of active view.");
} else {
viewDirection = Base::Vector3d(0.0, 1.0, 0.0);
upDirection = Base::Vector3d(0.0, 0.0, 1.0);
}
Base::Vector3d textDirection = elementDirection.Cross(viewDirection);
if (textDirection.Length() < tolerance) {
// either elementDirection and viewDirection are parallel or one of them is null.
textDirection = elementDirection.Cross(upDirection);
}
return textDirection.Normalize();
}
//! true if the subject of this measurement is visible. For Measures that have multiple object subject,
//! all of the subjects must be visible.
bool ViewProviderMeasureBase::isSubjectVisible()
{
Gui::Document* guiDoc = nullptr;
try {
guiDoc = this->getDocument();
} catch (const Base::RuntimeError& e) {
Base::Console().Log("ViewProviderMeasureBase::isSubjectVisible: Could not get document\n");
return false;
}
// we need these things to proceed
if (!getMeasureObject() ||
getMeasureObject()->getSubject().empty() ||
!guiDoc ) {
return false;
}
for (auto & obj : getMeasureObject()->getSubject()) {
Gui::ViewProvider* vp = guiDoc->getViewProvider(obj);
if (!vp || !vp->isVisible()) {
return false;
}
}
// all of the subject objects are visible
return true;
}
//! gets called when the subject object issues a signalChanged (ie a property change). We are only interested in the subject's
//! Visibility property
void ViewProviderMeasureBase::onSubjectVisibilityChanged(const App::DocumentObject& docObj, const App::Property& prop)
{
if (docObj.isRemoving()) {
return;
}
std::string propName = prop.getName();
if (propName == "Visibility") {
if (!docObj.Visibility.getValue()) {
// show ourselves only if subject is visible
setVisible(false);
} else {
// here, we don't know if we should be visible or not, so we have to check the whole subject
setVisible(isSubjectVisible());
}
}
}
//NOLINTBEGIN
PROPERTY_SOURCE(MeasureGui::ViewProviderMeasure, MeasureGui::ViewProviderMeasureBase)
//NOLINTEND
//! the general purpose view provider. handles area, length, etc - any measure without a specialized VP
ViewProviderMeasure::ViewProviderMeasure()
{
sPixmap = "umf-measurement";
// setupSceneGraph for leader?
const size_t lineCount(3);
// indexes used to create the edges
// this makes a line from verts[0] to verts[1]
static const int32_t lines[lineCount] =
{
0,1,-1
};
pCoords = new SoCoordinate3();
pCoords->ref();
// Combine coordinates from baseTranslation and labelTranslation
auto engineCat = new SoConcatenate(SoMFVec3f::getClassTypeId());
auto origin = new SoSFVec3f();
origin->setValue(0,0,0);
engineCat->input[0]->connectFrom(origin);
engineCat->input[1]->connectFrom(&pLabelTranslation->translation);
pCoords->point.setNum(engineCat->output->getNumConnections());
pCoords->point.connectFrom(engineCat->output);
pLines = new SoIndexedLineSet();
pLines->ref();
pLines->coordIndex.setNum(lineCount);
pLines->coordIndex.setValues(0, lineCount, lines);
auto lineSep = pLineSeparator;
lineSep->addChild(pCoords);
lineSep->addChild(pLines);
auto points = new SoMarkerSet();
points->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CROSS",
Gui::ViewParams::instance()->getMarkerSize());
points->numPoints=1;
lineSep->addChild(points);
// Connect dragger local orientation to view orientation
Gui::View3DInventor* view = nullptr;
try {
view = dynamic_cast<Gui::View3DInventor*>(this->getActiveView());
} catch (const Base::RuntimeError& e) {
Base::Console().Log("ViewProviderMeasure::ViewProviderMeasure: Could not get active view\n");
}
if (view) {
Gui::View3DInventorViewer* viewer = view->getViewer();
auto renderManager = viewer->getSoRenderManager();
auto cam = renderManager->getCamera();
pDraggerOrientation->rotation.connectFrom(&cam->orientation);
}
}
ViewProviderMeasure::~ViewProviderMeasure()
{
pCoords->unref();
pLines->unref();
}
void ViewProviderMeasure::positionAnno(const Measure::MeasureBase* measureObject) {
(void)measureObject;
// Initialize the text position
Base::Vector3d textPos = getTextPosition();
auto srcVec = SbVec3f(textPos.x, textPos.y, textPos.z);
// Translate the position by the local dragger matrix (pDraggerOrientation)
Gui::View3DInventor* view = nullptr;
try {
view = dynamic_cast<Gui::View3DInventor*>(this->getActiveView());
} catch (const Base::RuntimeError& e) {
Base::Console().Log("ViewProviderMeasure::positionAnno: Could not get active view\n");
}
if(!view){
return;
}
Gui::View3DInventorViewer* viewer = view->getViewer();
auto gma = SoGetMatrixAction(viewer->getSoRenderManager()->getViewportRegion());
gma.apply(pDraggerOrientation);
auto mat = gma.getMatrix();
SbVec3f destVec(0, 0, 0);
mat.multVecMatrix(srcVec, destVec);
setLabelTranslation(destVec);
updateView();
}
void ViewProviderMeasure::onChanged(const App::Property* prop)
{
if (pcObject == nullptr) {
return;
}
ViewProviderMeasureBase::onChanged(prop);
}
//! repaint the annotation
void ViewProviderMeasure::redrawAnnotation()
{
// point on element
Base::Vector3d basePos = getBasePosition();
pcTransform->translation.setValue(SbVec3f(basePos.x, basePos.y, basePos.z));
setLabelValue(getMeasureObject()->getResultString());
ViewProviderMeasureBase::redrawAnnotation();
ViewProviderDocumentObject::updateView();
}
Base::Vector3d ViewProviderMeasure::getBasePosition(){
auto measureObject = getMeasureObject();
Base::Placement placement = measureObject->getPlacement();
return placement.getPosition();
}
Base::Vector3d ViewProviderMeasure::getTextPosition(){
constexpr float DefaultLeaderLength{20.0};
auto basePoint = getBasePosition();
Base::Vector3d textDirection(1.0, 1.0, 1.0);
textDirection.Normalize();
return basePoint + textDirection * DefaultLeaderLength;
}
//! called by the system when it is time to display this measure
void ViewProviderMeasureBase::show()
{
if (isSubjectVisible()) {
// only show the annotation if the subject is visible.
// this avoids disconnected annotations floating in space.
ViewProviderDocumentObject::show();
}
}

View File

@@ -0,0 +1,159 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 GUI_VIEWPROVIDER_MEASUREMENTBASE_H
#define GUI_VIEWPROVIDER_MEASUREMENTBASE_H
#include <Mod/Measure/MeasureGlobal.h>
#include <QString>
#include <App/Application.h>
#include <App/PropertyStandard.h>
#include <Base/Parameter.h>
#include <Gui/ViewProviderDocumentObject.h>
#include <Gui/SoTextLabel.h>
#include <Mod/Measure/App/MeasureBase.h>
//NOLINTBEGIN
class SbVec2s;
class SoFontStyle;
class SoBaseColor;
class SoText2;
class SoTranslation;
class SoPickStyle;
class SoCoordinate3;
class SoIndexedLineSet;
class SoTranslate2Dragger;
//NOLINTEND
namespace MeasureGui {
//NOLINTBEGIN
class MeasureGuiExport ViewProviderMeasureBase :public Gui::ViewProviderDocumentObject
{
PROPERTY_HEADER_WITH_OVERRIDE(ViewProviderMeasureBase);
public:
/// constructor.
ViewProviderMeasureBase();
/// destructor.
~ViewProviderMeasureBase() override;
// Display properties
App::PropertyColor TextColor;
App::PropertyColor TextBackgroundColor;
App::PropertyColor LineColor;
App::PropertyInteger FontSize;
//NOLINTEND
/**
* Attaches the document object to this view provider.
*/
bool isPartOfPhysicalObject() const override {return false;};
void attach(App::DocumentObject *pcObj) override;
void updateData(const App::Property* prop) override;
virtual void positionAnno(const Measure::MeasureBase* measureObject);
void finishRestoring() override;
bool useNewSelectionModel() const override {return true;}
std::vector<std::string> getDisplayModes() const override;
void setDisplayMode(const char* ModeName) override;
/// Show the annotation in the 3d window
void show() override;
virtual void redrawAnnotation();
Measure::MeasureBase* getMeasureObject();
virtual bool isSubjectVisible();
static Base::Vector3d toVector3d(SbVec3f svec) { return Base::Vector3d(svec[0], svec[1], svec[2]); }
static SbVec3f toSbVec3f(Base::Vector3d vec3) { return SbVec3f(vec3.x, vec3.y, vec3.z); }
void onSubjectVisibilityChanged(const App::DocumentObject& docObj, const App::Property& prop);
void connectToSubject(App::DocumentObject* subject);
void connectToSubject(std::vector<App::DocumentObject*> subject);
protected:
static void draggerChangedCallback(void* data, SoDragger*);
void onChanged(const App::Property* prop) override;
virtual void onLabelMoved() {};
void setLabelValue(const Base::Quantity& value);
void setLabelValue(const QString& value);
void setLabelTranslation(const SbVec3f& position);
SoPickStyle* getSoPickStyle();
SoDrawStyle* getSoLineStylePrimary();
SoDrawStyle* getSoLineStyleSecondary();
SoSeparator* getSoSeparatorText();
static constexpr double defaultTolerance = 10e-6;
virtual Base::Vector3d getTextDirection(Base::Vector3d elementDirection, double tolerance = defaultTolerance);
// TODO: getters & setters and move variables to private?
bool _mShowTree = true;
Gui::SoFrameLabel * pLabel;
SoTranslate2Dragger* pDragger;
SoTransform* pDraggerOrientation;
SoTransform * pLabelTranslation;
SoBaseColor * pColor;
SoSeparator* pRootSeparator;
SoSeparator* pTextSeparator;
SoSeparator* pLineSeparator;
SoSeparator* pLineSeparatorSecondary;
private:
boost::signals2::connection _mVisibilityChangedConnection;
};
//NOLINTBEGIN
class MeasureGuiExport ViewProviderMeasure : public MeasureGui::ViewProviderMeasureBase
{
PROPERTY_HEADER_WITH_OVERRIDE(MeasureGui::ViewProviderMeasure);
//NOLINTEND
public:
/// Constructor
ViewProviderMeasure();
~ViewProviderMeasure() override;
void redrawAnnotation() override;
void positionAnno(const Measure::MeasureBase* measureObject) override;
protected:
void onChanged(const App::Property* prop) override;
virtual Base::Vector3d getBasePosition();
virtual Base::Vector3d getTextPosition();
private:
SoCoordinate3 * pCoords;
SoIndexedLineSet * pLines;
};
} // namespace Gui
#endif // GUI_VIEWPROVIDER_MEASUREMENTBASE_H

View File

@@ -0,0 +1,222 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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_
# include <sstream>
# include <QApplication>
# include <Inventor/engines/SoCalculator.h>
# include <Inventor/engines/SoConcatenate.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/SoMarkerSet.h>
# include <Inventor/nodes/SoPickStyle.h>
# include <Inventor/nodes/SoText2.h>
# include <Inventor/nodes/SoTranslation.h>
#endif
#include <Gui/Inventor/MarkerBitmaps.h>
#include <App/Document.h>
#include <App/MeasureDistance.h>
#include <Base/Console.h>
#include <Base/Quantity.h>
#include "Mod/Measure/App/MeasureDistance.h"
#include <Mod/Measure/App/Preferences.h>
#include "ViewProviderMeasureDistance.h"
#include "Gui/Application.h"
#include <Gui/Command.h>
#include "Gui/Document.h"
#include "Gui/ViewParams.h"
using namespace Gui;
using namespace MeasureGui;
using namespace Measure;
PROPERTY_SOURCE(MeasureGui::ViewProviderMeasureDistance, MeasureGui::ViewProviderMeasureBase)
SbMatrix ViewProviderMeasureDistance::getMatrix() {
auto object = dynamic_cast<Measure::MeasureDistance*>(getObject());
auto vec1 = object->Position1.getValue();
auto vec2 = object->Position2.getValue();
const double tolerance(10.0e-6);
SbVec3f origin = toSbVec3f((vec2 + vec1) / 2);
Base::Vector3d localXAxis = (vec2 - vec1).Normalize();
Base::Vector3d localYAxis = getTextDirection(localXAxis, tolerance).Normalize();
// X and Y axis have to be 90° to eachother
assert(fabs(localYAxis.Dot(localXAxis)) < tolerance);
Base::Vector3d localZAxis = localYAxis.Cross(localXAxis).Normalize();
SbMatrix matrix = SbMatrix(
localXAxis.x, localXAxis.y, localXAxis.z, 0,
localYAxis.x, localYAxis.y, localYAxis.z ,0,
localZAxis.x, localZAxis.y, localZAxis.z, 0,
// 0,0,0,1
origin[0], origin[1], origin[2], 1
);
return matrix;
}
//! calculate a good direction from the elements being measured to the annotation text based on the layout
//! of the elements and its relationship with the cardinal axes and the view direction. elementDirection
//! is expected to be a normalized vector.
//! an example of an elementDirection would be the vector from the start of a line to the end.
Base::Vector3d ViewProviderMeasureDistance::getTextDirection(Base::Vector3d elementDirection, double tolerance)
{
const Base::Vector3d stdX(1.0, 0.0, 0.0);
const Base::Vector3d stdY(0.0, 1.0, 0.0);
const Base::Vector3d stdZ(0.0, 0.0, 1.0);
Base::Vector3d textDirection = elementDirection.Cross(stdX);
if (textDirection.Length() < tolerance) {
textDirection = elementDirection.Cross(stdY);
}
if (textDirection.Length() < tolerance) {
textDirection = elementDirection.Cross(stdZ);
}
textDirection.Normalize();
if (textDirection.Dot(stdZ) < 0.0) {
textDirection = textDirection * -1.0;
}
return textDirection.Normalize();
}
ViewProviderMeasureDistance::ViewProviderMeasureDistance()
{
sPixmap = "umf-measurement";
// vert indexes used to create the annotation lines
const size_t lineCount(3);
static const int32_t lines[lineCount] =
{
2,3,-1 // dimension line
};
const size_t lineCountSecondary(9);
static const int32_t linesSecondary[lineCountSecondary] = {
0,2,-1, // extension line 1
1,3,-1, // extension line 2
2,4,-1 // label helper line
};
// Line Coordinates
// 0-1 points on shape (dimension points)
// 2-3 ends of extension lines/dimension line
// 4 label position
pCoords = new SoCoordinate3();
pCoords->ref();
auto engineCoords = new SoCalculator();
engineCoords->a.connectFrom(&fieldDistance);
engineCoords->A.connectFrom(&pLabelTranslation->translation);
engineCoords->expression.setValue("ta=a/2; tb=A[1]; oA=vec3f(ta, 0, 0); oB=vec3f(-ta, 0, 0); "
"oC=vec3f(ta, tb, 0); oD=vec3f(-ta, tb, 0)");
auto engineCat = new SoConcatenate(SoMFVec3f::getClassTypeId());
engineCat->input[0]->connectFrom(&engineCoords->oA);
engineCat->input[1]->connectFrom(&engineCoords->oB);
engineCat->input[2]->connectFrom(&engineCoords->oC);
engineCat->input[3]->connectFrom(&engineCoords->oD);
engineCat->input[4]->connectFrom(&pLabelTranslation->translation);
pCoords->point.connectFrom(engineCat->output);
pCoords->point.setNum(engineCat->output->getNumConnections());
pLines = new SoIndexedLineSet();
pLines->ref();
pLines->coordIndex.setNum(lineCount);
pLines->coordIndex.setValues(0, lineCount, lines);
pLineSeparator->addChild(pCoords);
pLineSeparator->addChild(pLines);
// Secondary Lines
auto lineSetSecondary = new SoIndexedLineSet();
lineSetSecondary->coordIndex.setNum(lineCountSecondary);
lineSetSecondary->coordIndex.setValues(0, lineCountSecondary, linesSecondary);
pLineSeparatorSecondary->addChild(pCoords);
pLineSeparatorSecondary->addChild(lineSetSecondary);
auto points = new SoMarkerSet();
points->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CROSS",
ViewParams::instance()->getMarkerSize());
points->numPoints=2;
pLineSeparator->addChild(points);
}
ViewProviderMeasureDistance::~ViewProviderMeasureDistance()
{
pCoords->unref();
pLines->unref();
}
Measure::MeasureDistance* ViewProviderMeasureDistance::getMeasureDistance()
{
Measure::MeasureDistance* feature = dynamic_cast<Measure::MeasureDistance*>(pcObject);
if (!feature) {
throw Base::RuntimeError("Feature not found for ViewProviderMeasureDistance");
}
return feature;
}
//! repaint the annotation
void ViewProviderMeasureDistance::redrawAnnotation()
{
auto object = dynamic_cast<Measure::MeasureDistance*>(getObject());
auto vec1 = object->Position1.getValue();
auto vec2 = object->Position2.getValue();
// Set the distance
fieldDistance = (vec2 - vec1).Length();
setLabelValue(object->Distance.getQuantityValue().getUserString());
// Set matrix
SbMatrix matrix = getMatrix();
pcTransform->setMatrix(matrix);
ViewProviderMeasureBase::redrawAnnotation();
updateView();
}
void ViewProviderMeasureDistance::positionAnno(const Measure::MeasureBase* measureObject) {
(void)measureObject;
setLabelTranslation(SbVec3f(0, 10, 0));
}

View File

@@ -0,0 +1,69 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 MEASUREGUI_VIEWPROVIDERMEASUREDISTANCE_H
#define MEASUREGUI_VIEWPROVIDERMEASUREDISTANCE_H
#include <Mod/Measure/MeasureGlobal.h>
#include <QObject>
#include <Mod/Measure/App/MeasureDistance.h>
#include "ViewProviderMeasureBase.h"
class SoCoordinate3;
class SoIndexedLineSet;
namespace MeasureGui
{
class MeasureGuiExport ViewProviderMeasureDistance : public MeasureGui::ViewProviderMeasureBase
{
PROPERTY_HEADER_WITH_OVERRIDE(MeasureGui::ViewProviderMeasureDistance);
public:
/// Constructor
ViewProviderMeasureDistance();
~ViewProviderMeasureDistance() override;
Measure::MeasureDistance* getMeasureDistance();
void redrawAnnotation() override;
void positionAnno(const Measure::MeasureBase* measureObject) override;
protected:
Base::Vector3d getTextDirection(Base::Vector3d elementDirection, double tolerance = defaultTolerance) override;
private:
SoCoordinate3 * pCoords;
SoIndexedLineSet * pLines;
SoSFFloat fieldDistance;
SbMatrix getMatrix();
};
} //namespace MeasureGui
#endif // MEASUREGUI_VIEWPROVIDERMEASUREDISTANCE_H

View File

@@ -0,0 +1,43 @@
# /***************************************************************************
# * Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
# * *
# * 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/>. *
# * *
# **************************************************************************/
# Import the measure module here in order to ensure that default measurement types are loaded during startup.
# Note that they won't auto-load in gui-less mode. Ideally they would be loaded from "Init.py", similar
# to how the import/export types are registered. This would require to register measurement types from
# python which could complicate things further.
import Measure
from MeasureCOM import makeMeasureCOM, MeasureCOM
# Expose create functions
Measure.makeMeasureCOM = makeMeasureCOM
# Register python measure types
import FreeCAD
FreeCAD.MeasureManager.addMeasureType(
"CENTEROFMASS",
"Center of Mass",
MeasureCOM,
)

View File

@@ -0,0 +1,152 @@
# /***************************************************************************
# * Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
# * *
# * 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/>. *
# * *
# **************************************************************************/
import FreeCAD
from FreeCAD import Units, Placement
from UtilsMeasure import MeasureBasePython
from PySide.QtCore import QT_TRANSLATE_NOOP
__title__="Measure Center of Mass Object"
__author__ = "David Friedli"
__url__ = "http://www.freecad.org"
"""
The Measure cpp object defines a result and a placement property. The Python measure type
adds it's own specific properties. Once the object is recomputed the parent properties are updated
based on the specific python properties.
We'll need some kind of interface for the measure command which exposes "parseSelection", "isValidSelection" etc.
"""
def makeMeasureCOM(name="CenterOfMass"):
'''makeMeasureCOM(name): make a CenterofMass measurement'''
obj = FreeCAD.ActiveDocument.addObject("Measure::MeasurePython", name)
MeasureCOM(obj)
return obj
class MeasureCOM(MeasureBasePython):
"The MeasureCOM object"
def __init__(self, obj):
obj.Proxy = self
obj.addProperty("App::PropertyLinkSubGlobal", "Element", "", QT_TRANSLATE_NOOP("App::Property", "Element to measure"))
obj.addProperty("App::PropertyPosition", "Result", "", QT_TRANSLATE_NOOP("App::PropertyVector", "The result location"))
@classmethod
def isValidSelection(cls, selection):
if not len(selection) == 1:
return False
element = selection[0]
ob = element["object"]
subName = element["subName"]
if not ob:
return
sub = ob.getSubObject(subName)
if not sub:
return
if not hasattr(sub, "CenterOfMass"):
return
return True
@classmethod
def isPrioritySelection(cls, selection):
return False
@classmethod
def getInputProps(cls):
return ("Element",)
def getSubject(self, obj):
if not obj:
return ()
element = obj.Element
if not element:
return ()
ob = element[0]
if not ob:
return ()
return (ob,)
def parseSelection(self, obj, selection):
item = selection[0]
o = item["object"]
obj.Element = (o, item["subName"])
def getResultString(self, obj):
values = [Units.Quantity(v, Units.Length).getUserPreferred()[0] for v in obj.Result]
return "COM\nX: {}\nY: {}\nZ: {}".format(*values)
def execute(self, obj):
element = obj.Element
if not element:
return
ob = element[0]
subElements = element[1]
if subElements:
subName = subElements[0]
sub = ob.getSubObject(subName)
if not sub or not hasattr(sub, "CenterOfMass"):
return
com = sub.CenterOfMass
else:
# Get Center of Mass of the object
if not hasattr(ob, "Shape"):
return
shape = ob.Shape
if not hasattr(shape, "CenterOfMass"):
return
com = shape.CenterOfMass
obj.Result = com
placement = Placement()
placement.Base = com
obj.Placement = placement
def onChanged(self, obj, prop):
'''Do something when a property has changed'''
if prop == "Element":
self.execute(obj)

View File

@@ -29,9 +29,18 @@
// Measure
#ifndef MeasureExport
#ifdef Measure_EXPORTS
# define MeasureExport FREECAD_DECL_EXPORT
#define MeasureExport FREECAD_DECL_EXPORT
#else
# define MeasureExport FREECAD_DECL_IMPORT
#define MeasureExport FREECAD_DECL_IMPORT
#endif
#endif
// MeasureGui
#ifndef MeasureGuiExport
#ifdef MeasureGui_EXPORTS
# define MeasureGuiExport FREECAD_DECL_EXPORT
#else
# define MeasureGuiExport FREECAD_DECL_IMPORT
#endif
#endif

View File

@@ -0,0 +1,52 @@
# /***************************************************************************
# * Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
# * *
# * 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/>. *
# * *
# **************************************************************************/
from abc import ABC, abstractmethod, abstractclassmethod
from typing import List, Tuple
class MeasureBasePython(ABC):
@abstractclassmethod
def isValidSelection(cls, selection):
"""Returns True if the given selection is valid for this measurment"""
pass
@abstractclassmethod
def isPrioritySelection(cls, selection):
"""Returns True if creation of this measurment should be priorized over other measurements for the given selection"""
pass
@abstractclassmethod
def getInputProps(cls) -> Tuple[str]:
"""Returns all properties that the measurement's result depends on"""
return ()
@abstractmethod
def getSubject(self, obj) -> Tuple:
"""Returns all objects that are measured, this is used to autohide the measurement if the relevant elements are not visible"""
return []
@abstractmethod
def parseSelection(self, obj, selection):
"""Sets the measurements properties from the given selection"""
pass

View File

@@ -185,6 +185,8 @@
#include <ShapeUpgrade/UnifySameDomainPy.h>
#include <OCAF/ImportExportSettings.h>
#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);
}

View File

@@ -219,6 +219,8 @@ SET(Features_SRCS
AttachExtension.cpp
PrismExtension.cpp
PrismExtension.h
VectorAdapter.cpp
VectorAdapter.h
)
SOURCE_GROUP("Features" FILES ${Features_SRCS})
@@ -543,6 +545,9 @@ SET(Part_SRCS
TopoShapeOpCode.h
edgecluster.cpp
edgecluster.h
MeasureClient.cpp
MeasureClient.h
MeasureInfo.h
modelRefine.cpp
modelRefine.h
Tools.cpp

View File

@@ -0,0 +1,496 @@
/***************************************************************************
* Copyright (c) 2023 Wandererfan <wandererfan@gmail.com> *
* Copyright (c) 2023 Joel Meijering (EDG5000) <joel@meijering.email> *
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* *
* 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"
#include <Mod/Part/PartGlobal.h>
#include <string>
#include <TopoDS.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopAbs.hxx>
#include <BRepTools.hxx>
#include <BRep_Tool.hxx>
#include <BRepGProp.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <TopExp.hxx>
#include <GProp_GProps.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <gp_Circ.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <DatumFeature.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/MeasureManager.h>
#include <App/DocumentObserver.h>
#include <Base/Console.h>
#include <Base/Matrix.h>
#include <Base/Rotation.h>
#include <Base/Vector3D.h>
#include <Mod/Measure/App/MeasureAngle.h>
#include <Mod/Measure/App/MeasureDistance.h>
#include <Mod/Measure/App/MeasureLength.h>
#include <Mod/Measure/App/MeasurePosition.h>
#include <Mod/Measure/App/MeasureArea.h>
#include <Mod/Measure/App/MeasureRadius.h>
#include "VectorAdapter.h"
#include "PartFeature.h"
#include "MeasureClient.h"
using namespace Part;
// 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 App::SubObjectT& subject)
{
App::DocumentObject* obj = subject.getObject();
if (!obj) {
return {};
}
Part::TopoShape shape = Part::Feature::getTopoShape(obj);
if (shape.isNull()) {
return {};
}
auto geoFeat = dynamic_cast<App::GeoFeature*>(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 (!subject.getElementName()) {
return shape.getShape();
}
return shape.getSubShape(subject.getElementName(), true);
}
App::MeasureElementType PartMeasureTypeCb(App::DocumentObject* ob, const char* 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::PartMeasureTypeCb did not retrieve shape for %s, %s\n", ob->getNameInDocument(), 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 App::SubObjectT& subject, Base::Matrix4D *mat)
{
App::DocumentObject *obj = subject.getObject();
if (!obj) {
return {};
}
shapeOut = Part::Feature::getShape(obj, subject.getElementName(), true, mat);
return !shapeOut.IsNull();
}
Part::VectorAdapter buildAdapter(const App::SubObjectT& subject)
{
if (!subject.getObject()) {
return Part::VectorAdapter();
}
Base::Matrix4D mat;
TopoDS_Shape shape = Part::Feature::getShape(subject.getObject(), subject.getElementName(), true);
if (shape.IsNull()) {
// failure here on loading document with existing measurement.
Base::Console().Message("Part::buildAdapter did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return Part::VectorAdapter();
}
TopAbs_ShapeEnum shapeType = shape.ShapeType();
if (shapeType == TopAbs_EDGE)
{
TopoDS_Shape edgeShape;
if (!getShapeFromStrings(edgeShape, subject, &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, subject, &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 {};
}
MeasureLengthInfoPtr MeasureLengthHandler(const App::SubObjectT& subject)
{
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
// failure here on loading document with existing measurement.
Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return std::make_shared<MeasureLengthInfo>(false, 0.0, Base::Matrix4D());
}
TopAbs_ShapeEnum sType = shape.ShapeType();
if (sType != TopAbs_EDGE) {
return std::make_shared<MeasureLengthInfo>(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 std::make_shared<MeasureLengthInfo>(true, getLength(shape), placement);
}
MeasureRadiusInfoPtr MeasureRadiusHandler(const App::SubObjectT& subject)
{
Base::Placement placement; // curve center + orientation
Base::Vector3d pointOnCurve;
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
}
TopAbs_ShapeEnum sType = shape.ShapeType();
if (sType != TopAbs_EDGE) {
return std::make_shared<MeasureRadiusInfo>( 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 std::make_shared<MeasureRadiusInfo>( true, getRadius(shape), pointOnCurve, placement);
}
MeasureAreaInfoPtr MeasureAreaHandler(const App::SubObjectT& subject)
{
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
// failure here on loading document with existing measurement.
Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return std::make_shared<MeasureAreaInfo>(false, 0.0, Base::Matrix4D());
}
TopAbs_ShapeEnum sType = shape.ShapeType();
if (sType != TopAbs_FACE) {
return std::make_shared<MeasureAreaInfo>(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 std::make_shared<MeasureAreaInfo>(true, getFaceArea(shape), placement);
}
MeasurePositionInfoPtr MeasurePositionHandler(const App::SubObjectT& subject)
{
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
Base::Console().Message("MeasurePositionHandler did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return std::make_shared<MeasurePositionInfo>(false, Base::Vector3d());
}
TopAbs_ShapeEnum sType = shape.ShapeType();
if (sType != TopAbs_VERTEX) {
return std::make_shared<MeasurePositionInfo>(false, Base::Vector3d());
}
TopoDS_Vertex vertex = TopoDS::Vertex(shape);
auto point = BRep_Tool::Pnt(vertex);
return std::make_shared<MeasurePositionInfo>( true, Base::Vector3d(point.X(), point.Y(), point.Z()));
}
MeasureAngleInfoPtr MeasureAngleHandler(const App::SubObjectT& subject)
{
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
// failure here on loading document with existing measurement.
Base::Console().Message("MeasureAngleHandler did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return std::make_shared<MeasureAngleInfo>();
}
TopAbs_ShapeEnum sType = shape.ShapeType();
Part::VectorAdapter vAdapt = buildAdapter(subject);
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());
auto info = std::make_shared<MeasureAngleInfo>(vAdapt.isValid(), (Base::Vector3d)vAdapt, position);
return info;
}
MeasureDistanceInfoPtr MeasureDistanceHandler(const App::SubObjectT& subject)
{
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
// failure here on loading document with existing measurement.
Base::Console().Message("MeasureDistanceHandler did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return std::make_shared<MeasureDistanceInfo>();
}
// return a persistent copy of the TopoDS_Shape here as shape will go out of scope at end
BRepBuilderAPI_Copy copy(shape);
const TopoDS_Shape* newShape = new TopoDS_Shape(copy.Shape());
return std::make_shared<MeasureDistanceInfo>(true, newShape);
}
void Part::MeasureClient::initialize() {
App::MeasureManager::addMeasureHandler("Part", PartMeasureTypeCb);
}
Part::CallbackRegistrationList Part::MeasureClient::reportLengthCB()
{
CallbackRegistrationList callbacks;
callbacks.emplace_back("Part", "Length", MeasureLengthHandler);
callbacks.emplace_back("PartDesign", "Length", MeasureLengthHandler);
callbacks.emplace_back("Sketcher", "Length", MeasureLengthHandler);
return callbacks;
}
Part::CallbackRegistrationList Part::MeasureClient::reportPositionCB()
{
CallbackRegistrationList callbacks;
callbacks.emplace_back("Part", "Position", MeasurePositionHandler);
callbacks.emplace_back("PartDesign", "Position", MeasurePositionHandler);
callbacks.emplace_back("Sketcher", "Position", MeasurePositionHandler);
return callbacks;
}
Part::CallbackRegistrationList Part::MeasureClient::reportAreaCB()
{
CallbackRegistrationList callbacks;
callbacks.emplace_back("Part", "Area", MeasureAreaHandler);
callbacks.emplace_back("PartDesign", "Area", MeasureAreaHandler);
callbacks.emplace_back("Sketcher", "Area", MeasureAreaHandler);
return callbacks;
}
Part::CallbackRegistrationList Part::MeasureClient::reportAngleCB()
{
CallbackRegistrationList callbacks;
callbacks.emplace_back("Part", "Angle", MeasureAngleHandler);
callbacks.emplace_back("PartDesign", "Angle", MeasureAngleHandler);
callbacks.emplace_back("Sketcher", "Angle", MeasureAngleHandler);
return callbacks;
}
Part::CallbackRegistrationList Part::MeasureClient::reportDistanceCB()
{
CallbackRegistrationList callbacks;
callbacks.emplace_back("Part", "Distance", MeasureDistanceHandler);
callbacks.emplace_back("PartDesign", "Distance", MeasureDistanceHandler);
callbacks.emplace_back("Sketcher", "Distance", MeasureDistanceHandler);
return callbacks;
}
Part::CallbackRegistrationList Part::MeasureClient::reportRadiusCB()
{
CallbackRegistrationList callbacks;
callbacks.emplace_back("Part", "Radius", MeasureRadiusHandler);
callbacks.emplace_back("PartDesign", "Radius", MeasureRadiusHandler);
callbacks.emplace_back("Sketcher", "Radius", MeasureRadiusHandler);
return callbacks;
}

View File

@@ -0,0 +1,52 @@
/***************************************************************************
* Copyright (c) 2023 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 PART_MEASURE_H
#define PART_MEASURE_H
#include <Mod/Part/PartGlobal.h>
#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

View File

@@ -0,0 +1,160 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
/***************************************************************************
* Copyright (c) 2024 wandererfan <wandererfan at gmail dot 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/>. *
* *
**************************************************************************/
//! MeasureInfo.h
//! ancestor class and the various flavours of MeasureXXXXXInfo.
#ifndef PART_MEASUREINFO_H
#define PART_MEASUREINFO_H
#include <Mod/Part/PartGlobal.h>
#include <functional>
#include <string>
#include <vector>
#include <memory>
#include <App/DocumentObserver.h>
#include <Base/Vector3D.h>
#include <Base/Placement.h>
class TopoDS_Shape;
namespace Part {
class MeasureInfo;
using MeasureInfoPtr = std::shared_ptr<MeasureInfo>;
class MeasureAngleInfo;
using MeasureAngleInfoPtr = std::shared_ptr<MeasureAngleInfo>;
class MeasureAreaInfo;
using MeasureAreaInfoPtr = std::shared_ptr<MeasureAreaInfo>;
class MeasureDistanceInfo;
using MeasureDistanceInfoPtr = std::shared_ptr<MeasureDistanceInfo>;
class MeasureLengthInfo;
using MeasureLengthInfoPtr = std::shared_ptr<MeasureLengthInfo>;
class MeasurePositionInfo;
using MeasurePositionInfoPtr = std::shared_ptr<MeasurePositionInfo>;
class MeasureRadiusInfo;
using MeasureRadiusInfoPtr = std::shared_ptr<MeasureRadiusInfo>;
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<Part::MeasureInfoPtr (App::SubObjectT)>;
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<CallbackRegistrationRecord>;
} //end namespace Part
#endif

View File

@@ -0,0 +1,156 @@
/***************************************************************************
* Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 <App/Application.h>
#include <Mod/Part/PartGlobal.h>
#include "VectorAdapter.h"
#include "Base/Console.h"
#include <string>
#include "PrimitiveFeature.h"
#include "PartFeature.h"
#include <Standard_Type.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_ElementarySurface.hxx>
#include <TopExp.hxx>
#include <Geom_Line.hxx>
#include <GeomAPI_ProjectPointOnCurve.hxx>
#include <BRep_Tool.hxx>
namespace Part {
VectorAdapter::VectorAdapter() : status(false), vector()
{
}
VectorAdapter::VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn) :
status(false), vector(), origin(pickedPointIn)
{
Handle(Geom_Surface) surface = BRep_Tool::Surface(faceIn);
if (surface->IsKind(STANDARD_TYPE(Geom_ElementarySurface)))
{
Handle(Geom_ElementarySurface) eSurface = Handle(Geom_ElementarySurface)::DownCast(surface);
gp_Dir direction = eSurface->Axis().Direction();
vector = direction;
vector.Normalize();
if (faceIn.Orientation() == TopAbs_REVERSED) {
vector.Reverse();
}
if (surface->IsKind(STANDARD_TYPE(Geom_CylindricalSurface)) ||
surface->IsKind(STANDARD_TYPE(Geom_SphericalSurface))
)
{
origin = eSurface->Axis().Location().XYZ();
projectOriginOntoVector(pickedPointIn);
}
else {
origin = pickedPointIn + vector;
}
status = true;
}
}
VectorAdapter::VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn) :
status(false), vector(), origin(pickedPointIn)
{
TopoDS_Vertex firstVertex = TopExp::FirstVertex(edgeIn, Standard_True);
TopoDS_Vertex lastVertex = TopExp::LastVertex(edgeIn, Standard_True);
vector = convert(lastVertex) - convert(firstVertex);
if (vector.Magnitude() < Precision::Confusion()) {
return;
}
vector.Normalize();
status = true;
projectOriginOntoVector(pickedPointIn);
}
VectorAdapter::VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In) :
status(false), vector(), origin()
{
vector = convert(vertex2In) - convert(vertex1In);
vector.Normalize();
//build origin half way.
gp_Vec tempVector = (convert(vertex2In) - convert(vertex1In));
double mag = tempVector.Magnitude();
tempVector.Normalize();
tempVector *= (mag / 2.0);
origin = tempVector + convert(vertex1In);
status = true;
}
VectorAdapter::VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2) :
status(false), vector(), origin()
{
vector = vector2- vector1;
vector.Normalize();
//build origin half way.
gp_Vec tempVector = vector2 - vector1;
double mag = tempVector.Magnitude();
tempVector.Normalize();
tempVector *= (mag / 2.0);
origin = tempVector + vector1;
status = true;
}
void VectorAdapter::projectOriginOntoVector(const gp_Vec &pickedPointIn)
{
Handle(Geom_Curve) heapLine = new Geom_Line(origin.XYZ(), vector.XYZ());
gp_Pnt tempPoint(pickedPointIn.XYZ());
GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine);
if (projection.NbPoints() < 1) {
return;
}
origin.SetXYZ(projection.Point(1).XYZ());
}
VectorAdapter::operator gp_Lin() const
{
gp_Pnt tempOrigin;
tempOrigin.SetXYZ(origin.XYZ());
return gp_Lin(tempOrigin, gp_Dir(vector));
}
/*convert a vertex to vector*/
gp_Vec VectorAdapter::convert(const TopoDS_Vertex &vertex)
{
gp_Pnt point = BRep_Tool::Pnt(vertex);
gp_Vec out(point.X(), point.Y(), point.Z());
return out;
}
}

View File

@@ -0,0 +1,90 @@
/***************************************************************************
* Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 PART_VECTORADAPTER_H
#define PART_VECTORADAPTER_H
#include <Mod/Part/PartGlobal.h>
#include <TopoDS.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <gp_Lin.hxx>
#include <Base/Vector3D.h>
namespace Part
{
/*! @brief Convert to vector
*
* Used to construct a vector from various input types
*/
class VectorAdapter
{
public:
/*!default construction isValid is set to false*/
VectorAdapter();
/*!Build a vector from a faceIn
* @param faceIn vector will be normal to plane and equal to cylindrical axis.
* @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/
VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn);
/*!Build a vector from an edgeIn
* @param edgeIn vector will be lastPoint - firstPoint.
* @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/
VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn);
/*!Build a vector From 2 vertices.
*vector will be equal to @param vertex2In - @param vertex1In.*/
VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In);
/*!Build a vector From 2 vectors.
*vector will be equal to @param vector2 - @param vector1.*/
VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2);
/*!make sure no errors in vector construction.
* @return true = vector is good. false = vector is NOT good.*/
bool isValid() const {return status;}
/*!get the calculated vector.
* @return the vector. use isValid to ensure correct results.*/
operator gp_Vec() const {return vector;}//explicit bombs
/*!build occ line used for extrema calculation*/
operator gp_Lin() const;//explicit bombs
gp_Vec getPickPoint() const {return origin;}
operator Base::Vector3d() const {
return Base::Vector3d(vector.X(), vector.Y(), vector.Z());
}
static gp_Vec convert(const TopoDS_Vertex& vertex);
private:
void projectOriginOntoVector(const gp_Vec &pickedPointIn);
bool status;
gp_Vec vector;
gp_Vec origin;
};
} //namespace Part
#endif

View File

@@ -39,9 +39,7 @@
#include "PropertyEnumAttacherItem.h"
#include "DlgSettings3DViewPartImp.h"
#include "DlgSettingsGeneral.h"
#include "DlgSettingsMeasure.h"
#include "DlgSettingsObjectColor.h"
#include "TaskDimension.h"
#include "SoBrepEdgeSet.h"
#include "SoBrepFaceSet.h"
#include "SoBrepPointSet.h"
@@ -136,7 +134,6 @@ PyMOD_INIT_FUNC(PartGui)
Gui::BitmapFactory().addPath(QString::fromLatin1(":/icons/booleans"));
Gui::BitmapFactory().addPath(QString::fromLatin1(":/icons/create"));
Gui::BitmapFactory().addPath(QString::fromLatin1(":/icons/measure"));
Gui::BitmapFactory().addPath(QString::fromLatin1(":/icons/parametric"));
Gui::BitmapFactory().addPath(QString::fromLatin1(":/icons/tools"));
@@ -212,9 +209,6 @@ PyMOD_INIT_FUNC(PartGui)
PartGui::ViewProviderRuledSurface ::init();
PartGui::ViewProviderFace ::init();
PartGui::ViewProviderProjectOnSurface ::init();
PartGui::DimensionLinear ::initClass();
PartGui::DimensionAngular ::initClass();
PartGui::ArcEngine ::initClass();
PartGui::Workbench ::init();
auto manip = std::make_shared<PartGui::WorkbenchManipulator>();
@@ -238,7 +232,6 @@ PyMOD_INIT_FUNC(PartGui)
(void)new Gui::PrefPageProducer<PartGui::DlgSettingsGeneral>(QT_TRANSLATE_NOOP("QObject", "Part/Part Design"));
(void)new Gui::PrefPageProducer<PartGui::DlgSettings3DViewPart>(QT_TRANSLATE_NOOP("QObject", "Part/Part Design"));
(void)new Gui::PrefPageProducer<PartGui::DlgSettingsObjectColor>(QT_TRANSLATE_NOOP("QObject", "Part/Part Design"));
(void)new Gui::PrefPageProducer<PartGui::DlgSettingsMeasure>(QT_TRANSLATE_NOOP("QObject", "Part/Part Design"));
(void)new Gui::PrefPageProducer<PartGui::DlgImportExportIges>(QT_TRANSLATE_NOOP("QObject", "Import-Export"));
(void)new Gui::PrefPageProducer<PartGui::DlgImportExportStep>(QT_TRANSLATE_NOOP("QObject", "Import-Export"));
Gui::ViewProviderBuilder::add(

View File

@@ -57,7 +57,6 @@ set(PartGui_UIC_SRCS
DlgRevolution.ui
DlgSettings3DViewPart.ui
DlgSettingsGeneral.ui
DlgSettingsMeasure.ui
DlgSettingsObjectColor.ui
DlgProjectionOnSurface.ui
SectionCutting.ui
@@ -133,15 +132,9 @@ SET(PartGui_SRCS
DlgSettingsGeneral.cpp
DlgSettingsGeneral.h
DlgSettingsGeneral.ui
DlgSettingsMeasure.cpp
DlgSettingsMeasure.h
DlgSettingsMeasure.ui
DlgSettingsObjectColor.cpp
DlgSettingsObjectColor.h
DlgSettingsObjectColor.ui
DlgSettingsMeasure.cpp
DlgSettingsMeasure.h
DlgSettingsMeasure.ui
DlgProjectionOnSurface.cpp
DlgProjectionOnSurface.h
DlgProjectionOnSurface.ui
@@ -248,8 +241,6 @@ SET(PartGui_SRCS
TaskSweep.ui
TaskThickness.cpp
TaskThickness.h
TaskDimension.cpp
TaskDimension.h
TaskCheckGeometry.cpp
TaskCheckGeometry.h
TaskAttacher.h

View File

@@ -63,7 +63,6 @@
#include "Mirroring.h"
#include "SectionCutting.h"
#include "TaskCheckGeometry.h"
#include "TaskDimension.h"
#include "TaskLoft.h"
#include "TaskShapeBuilder.h"
#include "TaskSweep.h"
@@ -2123,225 +2122,6 @@ bool CmdColorPerFace::isActive()
return (hasActiveDocument() && !Gui::Control().activeDialog() && objectSelected);
}
//===========================================================================
// Part_Measure_Linear
//===========================================================================
DEF_STD_CMD_A(CmdMeasureLinear)
CmdMeasureLinear::CmdMeasureLinear()
: Command("Part_Measure_Linear")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Measure Linear");
sToolTipText = QT_TR_NOOP("Measure the linear distance between two points;\n"
"if edges or faces are picked, it will measure\n"
"between two vertices of them.");
sWhatsThis = "Part_Measure_Linear";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Linear";
}
void CmdMeasureLinear::activated(int iMsg)
{
Q_UNUSED(iMsg);
PartGui::goDimensionLinearRoot();
}
bool CmdMeasureLinear::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Measure_Angular
//===========================================================================
DEF_STD_CMD_A(CmdMeasureAngular)
CmdMeasureAngular::CmdMeasureAngular()
: Command("Part_Measure_Angular")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Measure Angular");
sToolTipText = QT_TR_NOOP("Measure the angle between two edges.");
sWhatsThis = "Part_Measure_Angular";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Angular";
}
void CmdMeasureAngular::activated(int iMsg)
{
Q_UNUSED(iMsg);
PartGui::goDimensionAngularRoot();
}
bool CmdMeasureAngular::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Measure_Refresh
//===========================================================================
DEF_STD_CMD_A(CmdMeasureRefresh)
CmdMeasureRefresh::CmdMeasureRefresh()
: Command("Part_Measure_Refresh")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Refresh");
sToolTipText = QT_TR_NOOP("Recalculate the dimensions\n"
"if the measured points have moved.");
sWhatsThis = "Part_Measure_Refresh";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Refresh";
}
void CmdMeasureRefresh::activated(int iMsg)
{
Q_UNUSED(iMsg);
PartGui::refreshDimensions();
}
bool CmdMeasureRefresh::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Measure_Clear_All
//===========================================================================
DEF_STD_CMD_A(CmdMeasureClearAll)
CmdMeasureClearAll::CmdMeasureClearAll()
: Command("Part_Measure_Clear_All")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Clear All");
sToolTipText = QT_TR_NOOP("Clear all dimensions from the active 3D view.");
sWhatsThis = "Part_Measure_Clear_All";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Clear_All";
}
void CmdMeasureClearAll::activated(int iMsg)
{
Q_UNUSED(iMsg);
PartGui::eraseAllDimensions();
}
bool CmdMeasureClearAll::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Measure_Toggle_All
//===========================================================================
DEF_STD_CMD_A(CmdMeasureToggleAll)
CmdMeasureToggleAll::CmdMeasureToggleAll()
: Command("Part_Measure_Toggle_All")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Toggle All");
sToolTipText = QT_TR_NOOP("Toggle on and off "
"all currently visible dimensions,\n"
"direct, orthogonal, and angular.");
sWhatsThis = "Part_Measure_Toggle_All";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Toggle_All";
}
void CmdMeasureToggleAll::activated(int iMsg)
{
Q_UNUSED(iMsg);
ParameterGrp::handle group = App::GetApplication().GetUserParameter().
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View");
bool visibility = group->GetBool("DimensionsVisible", true);
if (visibility)
group->SetBool("DimensionsVisible", false);
else
group->SetBool("DimensionsVisible", true);
}
bool CmdMeasureToggleAll::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Measure_Toggle_3D
//===========================================================================
DEF_STD_CMD_A(CmdMeasureToggle3d)
CmdMeasureToggle3d::CmdMeasureToggle3d()
: Command("Part_Measure_Toggle_3D")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Toggle 3D");
sToolTipText = QT_TR_NOOP("Toggle on and off "
"all direct dimensions,\n"
"including angular.");
sWhatsThis = "Part_Measure_Toggle_3D";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Toggle_3D";
}
void CmdMeasureToggle3d::activated(int iMsg)
{
Q_UNUSED(iMsg);
PartGui::toggle3d();
}
bool CmdMeasureToggle3d::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Measure_Toggle_Delta
//===========================================================================
DEF_STD_CMD_A(CmdMeasureToggleDelta)
CmdMeasureToggleDelta::CmdMeasureToggleDelta()
: Command("Part_Measure_Toggle_Delta")
{
sAppModule = "Part";
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Toggle Delta");
sToolTipText = QT_TR_NOOP("Toggle on and off "
"all orthogonal dimensions,\n"
"meaning that a direct dimension will be decomposed\n"
"into its X, Y, and Z components.");
sWhatsThis = "Part_Measure_Toggle_Delta";
sStatusTip = sToolTipText;
sPixmap = "Part_Measure_Toggle_Delta";
}
void CmdMeasureToggleDelta::activated(int iMsg)
{
Q_UNUSED(iMsg);
PartGui::toggleDelta();
}
bool CmdMeasureToggleDelta::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_BoxSelection
//===========================================================================
@@ -2486,13 +2266,6 @@ void CreatePartCommands()
rcCmdMgr.addCommand(new CmdPartThickness());
rcCmdMgr.addCommand(new CmdCheckGeometry());
rcCmdMgr.addCommand(new CmdColorPerFace());
rcCmdMgr.addCommand(new CmdMeasureLinear());
rcCmdMgr.addCommand(new CmdMeasureAngular());
rcCmdMgr.addCommand(new CmdMeasureRefresh());
rcCmdMgr.addCommand(new CmdMeasureClearAll());
rcCmdMgr.addCommand(new CmdMeasureToggleAll());
rcCmdMgr.addCommand(new CmdMeasureToggle3d());
rcCmdMgr.addCommand(new CmdMeasureToggleDelta());
rcCmdMgr.addCommand(new CmdBoxSelection());
rcCmdMgr.addCommand(new CmdPartProjectionOnSurface());
rcCmdMgr.addCommand(new CmdPartSectionCut());

View File

@@ -1,91 +0,0 @@
/***************************************************************************
* Copyright (c) 2022 *
* *
* 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 <Gui/Command.h>
#include "DlgSettingsMeasure.h"
#include "ui_DlgSettingsMeasure.h"
using namespace PartGui;
DlgSettingsMeasure::DlgSettingsMeasure(QWidget* parent)
: PreferencePage(parent) , ui(new Ui_DlgSettingsMeasure)
{
ui->setupUi(this);
connect(ui->pushButtonRefresh, &QPushButton::clicked, this, &DlgSettingsMeasure::onMeasureRefresh);
}
/**
* Destroys the object and frees any allocated resources
*/
DlgSettingsMeasure::~DlgSettingsMeasure() = default;
void DlgSettingsMeasure::saveSettings()
{
ui->dim3dColorButton->onSave();
ui->dimDeltaColorButton->onSave();
ui->dimAngularColorButton->onSave();
ui->fontSizeSpinBox->onSave();
ui->fontNameComboBox->onSave();
ui->fontStyleBoldCheckBox->onSave();
ui->fontStyleItalicCheckBox->onSave();
}
void DlgSettingsMeasure::loadSettings()
{
ui->dim3dColorButton->onRestore();
ui->dimDeltaColorButton->onRestore();
ui->dimAngularColorButton->onRestore();
ui->fontSizeSpinBox->onRestore();
ui->fontNameComboBox->onRestore();
ui->fontNameComboBox->addItems(QStringList({QString::fromUtf8("defaultFont")}));
ui->fontStyleBoldCheckBox->onRestore();
ui->fontStyleItalicCheckBox->onRestore();
}
/**
* Sets the strings of the subwidgets using the current language.
*/
void DlgSettingsMeasure::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
}
else {
QWidget::changeEvent(e);
}
}
void DlgSettingsMeasure::onMeasureRefresh()
{
DlgSettingsMeasure::saveSettings();
Gui::Command::runCommand(Gui::Command::Gui, "Gui.runCommand('Part_Measure_Refresh',0)");
}
#include "moc_DlgSettingsMeasure.cpp"

View File

@@ -1,279 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PartGui::DlgSettingsMeasure</class>
<widget class="QWidget" name="PartGui::DlgSettingsMeasure">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>282</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Measure</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Measurement settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="Gui::PrefColorButton" name="dim3dColorButton">
<property name="color">
<color>
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>Dimensions3dColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_1">
<property name="text">
<string>3D color</string>
</property>
</widget>
</item>
<item row="0" column="4">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="Gui::PrefColorButton" name="dimDeltaColorButton">
<property name="color">
<color>
<red>0</red>
<green>255</green>
<blue>0</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>DimensionsDeltaColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Delta color</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="Gui::PrefColorButton" name="dimAngularColorButton">
<property name="color">
<color>
<red>0</red>
<green>0</green>
<blue>255</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>DimensionsAngularColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Angular color</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="Gui::PrefSpinBox" name="fontSizeSpinBox">
<property name="maximumSize">
<size>
<width>80</width>
<height>28</height>
</size>
</property>
<property name="value">
<number>30</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>DimensionsFontSize</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Font size</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="Gui::PrefCheckBox" name="fontStyleBoldCheckBox">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Bold</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DimensionsFontStyleBold</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="Gui::PrefCheckBox" name="fontStyleItalicCheckBox">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Italic</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DimensionsFontStyleItalic</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="Gui::PrefFontBox" name="fontNameComboBox">
<property name="currentText">
<string>defaultFont</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>DimensionsFontName</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Part</cstring>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Font name</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButtonRefresh">
<property name="text">
<string>Refresh existing measurements</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::ColorButton</class>
<extends>QPushButton</extends>
<header>Gui/Widgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefSpinBox</class>
<extends>QSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefColorButton</class>
<extends>Gui::ColorButton</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFontBox</class>
<extends>QFontComboBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -30,15 +30,6 @@
<file>icons/create/Part_Torus.svg</file>
<file>icons/create/Part_Tube.svg</file>
</qresource>
<qresource>
<file>icons/measure/Part_Measure_Angular.svg</file>
<file>icons/measure/Part_Measure_Linear.svg</file>
<file>icons/measure/Part_Measure_Refresh.svg</file>
<file>icons/measure/Part_Measure_Step_Active.svg</file>
<file>icons/measure/Part_Measure_Step_Done.svg</file>
<file>icons/measure/Part_Measure_Toggle_3D.svg</file>
<file>icons/measure/Part_Measure_Toggle_Delta.svg</file>
</qresource>
<qresource>
<file>icons/parametric/Part_Box_Parametric.svg</file>
<file>icons/parametric/Part_Circle_Parametric.svg</file>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,659 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2943"
sodipodi:version="0.32"
inkscape:version="0.91 r13725"
sodipodi:docname="Part_Measure_Refresh.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2945">
<linearGradient
inkscape:collect="always"
id="linearGradient3961">
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop3963" />
<stop
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1"
id="stop3965" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3953">
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop3955" />
<stop
style="stop-color:#555753;stop-opacity:1"
offset="1"
id="stop3957" />
</linearGradient>
<linearGradient
id="linearGradient4158">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4160" />
<stop
style="stop-color:#f6f6f6;stop-opacity:0;"
offset="1"
id="stop4162" />
</linearGradient>
<linearGradient
id="linearGradient4122">
<stop
style="stop-color:#e3d328;stop-opacity:1;"
offset="0"
id="stop4124" />
<stop
style="stop-color:#e1dec3;stop-opacity:1;"
offset="1"
id="stop4126" />
</linearGradient>
<linearGradient
id="linearGradient4088">
<stop
style="stop-color:#e9cd23;stop-opacity:1;"
offset="0"
id="stop4090" />
<stop
style="stop-color:#040000;stop-opacity:0;"
offset="1"
id="stop4092" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4060">
<stop
style="stop-color:#ada9a9;stop-opacity:1;"
offset="0"
id="stop4062" />
<stop
style="stop-color:#ada9a9;stop-opacity:0;"
offset="1"
id="stop4064" />
</linearGradient>
<linearGradient
id="linearGradient4052">
<stop
style="stop-color:#ada9a9;stop-opacity:1;"
offset="0"
id="stop4054" />
<stop
style="stop-color:#ada9a9;stop-opacity:0;"
offset="1"
id="stop4056" />
</linearGradient>
<linearGradient
id="linearGradient4349">
<stop
style="stop-color:#898709;stop-opacity:1;"
offset="0"
id="stop4351" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop4353" />
</linearGradient>
<linearGradient
id="linearGradient5241">
<stop
style="stop-color:#212c45;stop-opacity:1;"
offset="0"
id="stop5243" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop5245" />
</linearGradient>
<linearGradient
id="linearGradient5227"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5229" />
</linearGradient>
<linearGradient
id="linearGradient3902">
<stop
style="stop-color:#000000;stop-opacity:0.58823532;"
offset="0"
id="stop3904" />
<stop
style="stop-color:#000000;stop-opacity:0.39215687;"
offset="1"
id="stop3906" />
</linearGradient>
<linearGradient
id="linearGradient3894">
<stop
style="stop-color:#45351d;stop-opacity:1;"
offset="0"
id="stop3896" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop3898" />
</linearGradient>
<linearGradient
id="linearGradient3886">
<stop
style="stop-color:#45351d;stop-opacity:1;"
offset="0"
id="stop3888" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop3890" />
</linearGradient>
<linearGradient
id="linearGradient3792">
<stop
style="stop-color:#aaaaaa;stop-opacity:1;"
offset="0"
id="stop3794" />
<stop
style="stop-color:#d2d2d2;stop-opacity:1;"
offset="1"
id="stop3796" />
</linearGradient>
<linearGradient
id="linearGradient3784">
<stop
style="stop-color:#bebebe;stop-opacity:1;"
offset="0"
id="stop3786" />
<stop
style="stop-color:#ffffff;stop-opacity:0.39215687;"
offset="1"
id="stop3788" />
</linearGradient>
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2951" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4158"
id="linearGradient3092"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-129.22376,-0.88388348)"
x1="419.99387"
y1="102.77802"
x2="458.7193"
y2="69.431564" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4052"
id="linearGradient3094"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(168.6744,65.825928)"
x1="138.99986"
y1="44.863674"
x2="92.497559"
y2="-14.356517" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4122"
id="linearGradient3096"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-88.034794,-1.0606602)"
x1="391.3074"
y1="120.81136"
x2="394.43201"
y2="112.43636" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4060"
id="linearGradient3098"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(168.6744,65.825928)"
x1="103.93729"
y1="49.179436"
x2="120.49899"
y2="0.21229285" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3953"
id="linearGradient3959"
x1="214.70918"
y1="80.886589"
x2="218.70918"
y2="104.88659"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3961"
id="linearGradient3967"
x1="196.70918"
y1="106.88659"
x2="190.70918"
y2="80.886589"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3953-5"
id="linearGradient3959-3"
x1="214.70918"
y1="80.886589"
x2="218.70918"
y2="104.88659"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
inkscape:collect="always"
id="linearGradient3953-5">
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop3955-6" />
<stop
style="stop-color:#555753;stop-opacity:1"
offset="1"
id="stop3957-2" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2831-2"
id="linearGradient4585"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.370336,0,0,1.3589114,0.02150968,-18.214919)"
x1="13.478554"
y1="10.612206"
x2="15.419417"
y2="19.115122" />
<linearGradient
id="linearGradient2831-2">
<stop
style="stop-color:#3465a4;stop-opacity:1;"
offset="0"
id="stop2833-3" />
<stop
id="stop2855-1"
offset="0.33333334"
style="stop-color:#5b86be;stop-opacity:1;" />
<stop
style="stop-color:#83a8d8;stop-opacity:0;"
offset="1"
id="stop2835-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2871"
id="linearGradient1488"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.370336,0,0,-1.3589114,64.512944,44.464873)"
x1="37.128052"
y1="29.729605"
x2="37.065414"
y2="26.194071" />
<linearGradient
id="linearGradient2871"
inkscape:collect="always">
<stop
id="stop2873"
offset="0"
style="stop-color:#3465a4;stop-opacity:1;" />
<stop
id="stop2875"
offset="1"
style="stop-color:#3465a4;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3063-2"
id="linearGradient4587"
gradientUnits="userSpaceOnUse"
x1="42.703487"
y1="20.547306"
x2="26.605606"
y2="33.634254" />
<linearGradient
id="linearGradient3063-2">
<stop
id="stop3065-6"
offset="0"
style="stop-color:#729fcf;stop-opacity:1" />
<stop
id="stop3067-0"
offset="1"
style="stop-color:#204a87;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2380-9"
id="linearGradient3034"
gradientUnits="userSpaceOnUse"
x1="41.791897"
y1="20.134634"
x2="23.705669"
y2="34.083359" />
<linearGradient
id="linearGradient2380-9">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop2382-4" />
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="1"
id="stop2384-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2380-9"
id="linearGradient3034-4"
gradientUnits="userSpaceOnUse"
x1="26.221533"
y1="31.125586"
x2="46.731483"
y2="21.766298" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2831-2"
id="linearGradient4295"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.370336,0,0,1.3589114,0.02150968,-18.214919)"
x1="13.478554"
y1="10.612206"
x2="15.419417"
y2="19.115122" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2871"
id="linearGradient4297"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.370336,0,0,-1.3589114,64.512944,44.464873)"
x1="37.128052"
y1="29.729605"
x2="37.065414"
y2="26.194071" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3063-2"
id="linearGradient4299"
gradientUnits="userSpaceOnUse"
x1="42.703487"
y1="20.547306"
x2="26.605606"
y2="33.634254" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.6761244"
inkscape:cx="44.088329"
inkscape:cy="36.738683"
inkscape:current-layer="g3629"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1375"
inkscape:window-height="876"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:snap-bbox="false"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid3059"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2948">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[blobfish]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Part_Measure_Linear</dc:title>
<dc:date>2013-12-17</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Measure_Linear.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3629"
transform="translate(-256.70919,-66.886588)">
<path
style="fill:#e3d328;fill-opacity:1;stroke:#040400;stroke-width:0.08838835;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d=""
id="path4102"
inkscape:connector-curvature="0"
transform="translate(256.70919,66.886588)" />
<path
style="fill:#babdb6;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 285.70919,77.886588 -26,-4 c 0,11 0,26 2,36.000002 l 30,12 0,-6 -2,-4 c 8,-12.000002 0,-24.000002 -4,-34.000002 z"
id="path3100"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#555753;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 291.70919,121.88659 12,-4 0,-6 -12,4 z"
id="path3890"
inkscape:connector-curvature="0" />
<path
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 301.70919,107.88659 -12,4 2,4 12,-4 z"
id="path3892"
inkscape:connector-curvature="0" />
<path
style="fill:#888a85;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 285.70919,77.886588 c 4,10 12,22 4,34.000002 l 12,-4 c 8,-12.000002 0,-24.000002 -4,-34.000002 z"
id="path3894"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 259.70919,73.886588 12,-4 26,4 -12,4 z"
id="path3888"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:url(#linearGradient3967);fill-opacity:1;stroke:#d3d7cf;stroke-width:1.99999975999999990;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 284.30919,79.786588 -22.6,-3.9 c 0,10.043478 -0.0125,23.469561 1.8,32.600002 l 26.2,10.4 0,-2.5 -2.2,-4.5 c 8,-12.000002 1.2,-22.000002 -3.2,-32.100002 z"
id="path3100-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:url(#linearGradient3959);fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 288.30919,79.086588 c 4,10 9.4,18.3 5.4,29.300002 l 6.6,-2.2 c 6,-9.000002 1.4,-19.300002 -3.8,-29.900002 z"
id="path3894-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
sodipodi:type="arc"
style="fill:#555753;fill-opacity:1;stroke:#2e3436;stroke-width:2.25831866;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6"
id="path3932"
sodipodi:cx="-93"
sodipodi:cy="39"
sodipodi:rx="15"
sodipodi:ry="15"
d="m -78,39 a 15,15 0 0 1 -15,15 15,15 0 0 1 -15,-15 15,15 0 0 1 15,-15 15,15 0 0 1 15,15 z"
transform="matrix(0.79999998,0.19607832,0,0.9803916,350.10919,74.8866)" />
<path
style="fill:#edd400;fill-opacity:1;stroke:#302b00;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:1.6"
d="m 287.70919,97.827762 -24,-5.882349 c 0,0 -0.82229,-14.095015 12,-11.764699 11.12322,2.021527 12,17.647048 12,17.647048 z"
id="path3932-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsc" />
<path
style="fill:#edd400;fill-opacity:1;stroke:#302b00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 294.70919,90.886588 c 2,4 3,11.000002 1,15.000002 l 4,-1 c 2,-4 2,-11.000002 0,-15.000002 z"
id="path3894-7-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#edd400;stroke:#302b00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 37,51 8,-3 16,5 -6,4 z"
id="path4003"
inkscape:connector-curvature="0"
transform="translate(256.70919,66.886588)"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 61,53 0,4 -6,4 0,-4 z"
id="path4005"
inkscape:connector-curvature="0"
transform="translate(256.70919,66.886588)"
sodipodi:nodetypes="ccccc" />
<g
id="g4314">
<g
id="g3863"
transform="matrix(0.59299466,0,0,0.59299466,283.4333,77.218635)">
<path
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient4295);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4297);stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="m 27,-3.6915582 c 0,0 -12.247378,-0.8493196 -8.478954,13.4192502 l -10.534458,0 c 0,0 0.685168,-16.137073 19.013412,-13.4192502 z"
id="path2865-9"
inkscape:r_cx="true"
inkscape:r_cy="true"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<g
id="g1878-1"
transform="matrix(-0.79349441,-0.66481753,-0.67040672,0.78687903,77.66003,0.94046451)"
inkscape:r_cx="true"
inkscape:r_cy="true"
style="fill:url(#linearGradient4299);fill-opacity:1;stroke:#204a87;stroke-width:0.73280919;stroke-opacity:1">
<path
sodipodi:nodetypes="ccccccc"
id="path1880-27"
d="M 44.306783,50.229694 C 62.821497,35.818859 49.664587,13.411704 22.462411,12.49765 L 22.113843,3.1515478 7.6245439,20.496754 22.714328,33.219189 c 0,0 -0.251917,-9.88122 -0.251917,-9.88122 18.82976,0.998977 32.981627,14.071729 21.844372,26.891725 z"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient3034);fill-opacity:1;fill-rule:nonzero;stroke:#0b1521;stroke-width:3.26039815;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
inkscape:r_cx="true"
inkscape:r_cy="true"
inkscape:connector-curvature="0" />
</g>
<g
style="fill:none;stroke:#729fcf;stroke-width:0.73280919;stroke-opacity:1"
inkscape:r_cy="true"
inkscape:r_cx="true"
transform="matrix(-0.69686517,-0.58385766,-0.58876622,0.69105539,72.350404,1.0127423)"
id="g2805-0">
<path
inkscape:r_cy="true"
inkscape:r_cx="true"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:none;stroke:#729fcf;stroke-width:2.20148993;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:21;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="M 52.368857,42.344789 C 57.336994,33.465615 49.176003,12.601866 19.05552,12.672851 L 18.677956,5.6633463 7.4378077,19.282655 19.129354,29.167094 18.807724,20.554957 c 18.244937,0.381972 33.804002,9.457851 33.561133,21.789832 z"
id="path2807-9"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g3863-0"
transform="matrix(-0.59299466,0,0,-0.59299466,322.563,94.41548)">
<path
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient4585);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient1488);stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="m 27,-3.6915582 c 0,0 -12.247378,-0.8493196 -8.478954,13.4192502 l -10.534458,0 c 0,0 0.685168,-16.137073 19.013412,-13.4192502 z"
id="path2865-3"
inkscape:r_cx="true"
inkscape:r_cy="true"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<g
id="g1878-6"
transform="matrix(-0.79349441,-0.66481753,-0.67040672,0.78687903,77.66003,0.94046451)"
inkscape:r_cx="true"
inkscape:r_cy="true"
style="fill:url(#linearGradient4587);fill-opacity:1;stroke:#204a87;stroke-width:0.73280919;stroke-opacity:1">
<path
sodipodi:nodetypes="ccccccc"
id="path1880-2"
d="M 44.306783,50.229694 C 62.821497,35.818859 49.664587,13.411704 22.462411,12.49765 L 22.113843,3.1515478 7.6245439,20.496754 22.714328,33.219189 c 0,0 -0.251917,-9.88122 -0.251917,-9.88122 18.82976,0.998977 32.981627,14.071729 21.844372,26.891725 z"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient3034-4);fill-opacity:1;fill-rule:nonzero;stroke:#0b1521;stroke-width:3.26039815;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
inkscape:r_cx="true"
inkscape:r_cy="true"
inkscape:connector-curvature="0" />
</g>
<g
style="fill:none;stroke:#729fcf;stroke-width:0.73280919;stroke-opacity:1"
inkscape:r_cy="true"
inkscape:r_cx="true"
transform="matrix(-0.69686517,-0.58385766,-0.58876622,0.69105539,72.350404,1.0127423)"
id="g2805-4">
<path
inkscape:r_cy="true"
inkscape:r_cx="true"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:none;stroke:#729fcf;stroke-width:2.20148993;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:21;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="M 52.368857,42.344789 C 57.864671,33.591679 49.176003,12.601866 19.05552,12.672851 L 18.677956,5.6633463 7.4378077,19.282655 19.129354,29.167094 18.807724,20.554957 c 18.244937,0.381972 33.804002,9.457851 33.561133,21.789832 z"
id="path2807-5"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1,374 +0,0 @@
/***************************************************************************
* Copyright (c) 2013 Thomas Anderson <blobfish[at]gmx.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 TASKDIMENSION_H
#define TASKDIMENSION_H
#include <gp_Lin.hxx>
#include <gp_Vec.hxx>
#include <Inventor/engines/SoSubEngine.h>
#include <Inventor/engines/SoEngine.h>
#include <Inventor/fields/SoSFColor.h>
#include <Inventor/fields/SoSFFloat.h>
#include <Inventor/fields/SoSFMatrix.h>
#include <Inventor/fields/SoSFRotation.h>
#include <Inventor/fields/SoSFString.h>
#include <Inventor/fields/SoSFVec3f.h>
#include <Inventor/nodekits/SoSeparatorKit.h>
#include <Base/Matrix.h>
#include <Gui/TaskView/TaskDialog.h>
#include <Gui/TaskView/TaskView.h>
class TopoDS_Shape;
class TopoDS_Face;
class TopoDS_Edge;
class TopoDS_Vertex;
class gp_Pnt;
class BRepExtrema_DistShapeShape;
class QPushButton;
class QPixmap;
class QLabel;
namespace Gui{class View3dInventorViewer;}
namespace PartGui
{
/*!find shape from selection strings
* @param shapeOut search results.
* @param doc document name to search.
* @param object object name to search.
* @param sub sub-object name to search.
* @return signal if the search was successful.
*/
bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub, Base::Matrix4D *mat=nullptr);
/*!examine pre selection
* @param shape1 first shape in current selection
* @param shape2 second shape in current selection
* @return signal if preselection is valid. false means shape1 and shape2 are invalid.
*/
bool evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &shape2);
/*!start of the measure linear command*/
void goDimensionLinearRoot();
/*!does the measure and create dimensions without a dialog
* @param shape1 first shape.
* @param shape2 second shape.
* @todo incorporate some form of "adapt to topods_shape". so we can expand to other types outside OCC.
*/
void goDimensionLinearNoTask(const TopoDS_Shape &shape1, const TopoDS_Shape &shape2);
/*!prints results of measuring to console.
* @param measure object containing the measure information
*/
void dumpLinearResults(const BRepExtrema_DistShapeShape &measure);
/*!convenience function to get the viewer*/
Gui::View3DInventorViewer* getViewer();
/*!adds 3d and delta dimensions to the viewer
* @param measure object containing the measure information.
*/
void addLinearDimensions(const BRepExtrema_DistShapeShape &measure);
/*!creates one dimension from points with color
* @param point1 first point
* @param point2 second point
* @param color color of dimension
* @return an inventor node to add to a scenegraph
*/
SoNode* createLinearDimension(const gp_Pnt &point1, const gp_Pnt &point2, const SbColor &color);
/*!erases all the dimensions in the viewer.*/
void eraseAllDimensions();
/*!refresh all the dimensions in the viewer.*/
void refreshDimensions();
/*!toggles the display status of the 3d dimensions*/
void toggle3d();
/*!toggles the display status of the delta dimensions*/
void toggleDelta();
/*!make sure measure command isn't working with everything invisible. Confusing the user*/
void ensureSomeDimensionVisible();
/*!make sure angle measure command isn't working with 3d off. Confusing the user*/
void ensure3dDimensionVisible();
/*convert a vertex to vector*/
gp_Vec convert(const TopoDS_Vertex &vertex);
auto getDimensionsFontName();
auto getDimensionsFontSize();
class DimensionLinear : public SoSeparatorKit
{
SO_KIT_HEADER(DimensionLinear);
SO_KIT_CATALOG_ENTRY_HEADER(transformation);
SO_KIT_CATALOG_ENTRY_HEADER(annotate);
SO_KIT_CATALOG_ENTRY_HEADER(leftArrow);
SO_KIT_CATALOG_ENTRY_HEADER(rightArrow);
SO_KIT_CATALOG_ENTRY_HEADER(line);
SO_KIT_CATALOG_ENTRY_HEADER(textSep);
public:
DimensionLinear();
static void initClass();
SbBool affectsState() const override;
void setupDimension();
SoSFVec3f point1;
SoSFVec3f point2;
SoSFString text;
SoSFColor dColor;
protected:
SoSFRotation rotate;
SoSFFloat length;
SoSFVec3f origin;
private:
~DimensionLinear() override;
};
/*kit for anglular dimensions*/
class DimensionAngular : public SoSeparatorKit
{
SO_KIT_HEADER(DimensionAngular);
SO_KIT_CATALOG_ENTRY_HEADER(transformation);
SO_KIT_CATALOG_ENTRY_HEADER(annotate);
SO_KIT_CATALOG_ENTRY_HEADER(arrow1);
SO_KIT_CATALOG_ENTRY_HEADER(arrow2);
SO_KIT_CATALOG_ENTRY_HEADER(arcSep);
SO_KIT_CATALOG_ENTRY_HEADER(textSep);
public:
DimensionAngular();
static void initClass();
SbBool affectsState() const override;
SoSFFloat radius;//radians.
SoSFFloat angle;//radians.
SoSFString text;
SoSFColor dColor;
SoSFMatrix matrix;
void setupDimension();
private:
~DimensionAngular() override;
};
/*used for generating points for arc display*/
class ArcEngine : public SoEngine
{
SO_ENGINE_HEADER(ArcEngine);
public:
ArcEngine();
static void initClass();
SoSFFloat radius;
SoSFFloat angle;
SoSFFloat deviation;
SoEngineOutput points;
SoEngineOutput pointCount;
protected:
void evaluate() override;
private:
~ArcEngine() override = default;
void defaultValues(); //some non error values if something goes wrong.
};
/*! a widget with buttons and icons for a controlled selection process*/
class SteppedSelection : public QWidget
{
Q_OBJECT
public:
explicit SteppedSelection(const uint &buttonCountIn, QWidget *parent = nullptr);
~SteppedSelection() override;
QPushButton* getButton(const uint &index);
void setIconDone(const uint &index);
protected:
using ButtonIconPairType = std::pair<QPushButton *, QLabel *>;
std::vector<ButtonIconPairType> buttons;
QPixmap *stepActive;
QPixmap *stepDone;
private Q_SLOTS:
void selectionSlot(bool checked);
void buildPixmaps();
};
/*! just convenience container*/
class DimSelections
{
public:
enum ShapeType{None, Vertex, Edge, Face};
struct DimSelection
{
std::string documentName;
std::string objectName;
std::string subObjectName;
float x;
float y;
float z;
ShapeType shapeType;
};
std::vector<DimSelection> selections;
};
/*!widget for buttons controlling the display of dimensions*/
class DimensionControl : public QWidget
{
Q_OBJECT
public:
explicit DimensionControl(QWidget* parent);
QPushButton *resetButton;
public Q_SLOTS:
void toggle3dSlot(bool);
void toggleDeltaSlot(bool);
void clearAllSlot(bool);
};
/*!linear dialog*/
class TaskMeasureLinear : public Gui::TaskView::TaskDialog, public Gui::SelectionObserver
{
Q_OBJECT
public:
TaskMeasureLinear();
~TaskMeasureLinear() override;
QDialogButtonBox::StandardButtons getStandardButtons() const override
{return QDialogButtonBox::Close;}
bool isAllowedAlterDocument() const override {return false;}
bool needsFullSpace() const override {return false;}
protected:
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
protected Q_SLOTS:
void selection1Slot(bool checked);
void selection2Slot(bool checked);
void resetDialogSlot(bool);
void toggle3dSlot(bool);
void toggleDeltaSlot(bool);
void clearAllSlot(bool);
void selectionClearDelayedSlot();
public:
static void buildDimension(const DimSelections &sel1, const DimSelections &sel2);
private:
void setUpGui();
void buildDimension();
void clearSelectionStrings();
DimSelections selections1;
DimSelections selections2;
uint buttonSelectedIndex;
SteppedSelection *stepped;
};
/*! @brief Convert to vector
*
* Used to construct a vector from various input types
*/
class VectorAdapter
{
public:
/*!default construction isValid is set to false*/
VectorAdapter();
/*!Build a vector from a faceIn
* @param faceIn vector will be normal to plane and equal to cylindrical axis.
* @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/
VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn);
/*!Build a vector from an edgeIn
* @param edgeIn vector will be lastPoint - firstPoint.
* @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/
VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn);
/*!Build a vector From 2 vertices.
*vector will be equal to @param vertex2In - @param vertex1In.*/
VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In);
/*!Build a vector From 2 vectors.
*vector will be equal to @param vector2 - @param vector1.*/
VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2);
/*!make sure no errors in vector construction.
* @return true = vector is good. false = vector is NOT good.*/
bool isValid() const {return status;}
/*!get the calculated vector.
* @return the vector. use isValid to ensure correct results.*/
operator gp_Vec() const {return vector;}//explicit bombs
/*!build occ line used for extrema calculation*/
operator gp_Lin() const;//explicit bombs
gp_Vec getPickPoint() const {return origin;}
private:
void projectOriginOntoVector(const gp_Vec &pickedPointIn);
bool status;
gp_Vec vector;
gp_Vec origin;
};
/*!angular dialog class*/
class TaskMeasureAngular : public Gui::TaskView::TaskDialog, public Gui::SelectionObserver
{
Q_OBJECT
public:
TaskMeasureAngular();
~TaskMeasureAngular() override;
QDialogButtonBox::StandardButtons getStandardButtons() const override
{return QDialogButtonBox::Close;}
bool isAllowedAlterDocument() const override {return false;}
bool needsFullSpace() const override {return false;}
protected:
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
protected Q_SLOTS:
void selection1Slot(bool checked);
void selection2Slot(bool checked);
void resetDialogSlot(bool);
void toggle3dSlot(bool);
void toggleDeltaSlot(bool);
void clearAllSlot(bool);
void selectionClearDelayedSlot();
public:
static void buildDimension(const DimSelections &sel1, const DimSelections &sel2);
private:
void buildDimension();
void setUpGui();
void clearSelection();
DimSelections selections1;
DimSelections selections2;
uint buttonSelectedIndex;
SteppedSelection *stepped;
static VectorAdapter buildAdapter(const DimSelections &selection);
};
/*!start of the measure angular command*/
void goDimensionAngularRoot();
/*!examine angular pre selection
* @param vector1Out first shape in current selection
* @param vector2Out second shape in current selection
* @return signal if preselection is valid. false means vector1Out and vector2Out are invalid.
*/
bool evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapter &vector2Out);
/*!build angular dimension*/
void goDimensionAngularNoTask(const VectorAdapter &vector1Adapter, const VectorAdapter &vector2Adapter);
}
#endif // TASKDIMENSION_H

View File

@@ -41,7 +41,6 @@ using namespace PartGui;
qApp->translate("Workbench", "Split");
qApp->translate("Workbench", "Compound");
qApp->translate("Workbench", "Create a copy");
qApp->translate("Workbench", "Measure");
#endif
/// @namespace PartGui @class Workbench
@@ -140,18 +139,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const
<< "Separator"
<< "Part_EditAttachment";
Gui::MenuItem* measure = new Gui::MenuItem;
root->insertItem(item,measure);
measure->setCommand("Measure");
*measure << "Part_Measure_Linear"
<< "Part_Measure_Angular"
<< "Separator"
<< "Part_Measure_Refresh"
<< "Part_Measure_Clear_All"
<< "Part_Measure_Toggle_All"
<< "Part_Measure_Toggle_3D"
<< "Part_Measure_Toggle_Delta";
Gui::MenuItem* view = root->findItem("&View");
if (view) {
Gui::MenuItem* appr = view->findItem("Std_RandomColor");
@@ -210,17 +197,6 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Part_CheckGeometry"
<< "Part_Defeaturing";
Gui::ToolBarItem* measure = new Gui::ToolBarItem(root);
measure->setCommand("Measure");
*measure << "Part_Measure_Linear"
<< "Part_Measure_Angular"
<< "Separator"
<< "Part_Measure_Refresh"
<< "Part_Measure_Clear_All"
<< "Part_Measure_Toggle_All"
<< "Part_Measure_Toggle_3D"
<< "Part_Measure_Toggle_Delta";
return root;
}

View File

@@ -34,13 +34,6 @@ void WorkbenchManipulator::modifyMenuBar([[maybe_unused]] Gui::MenuItem* menuBar
addSectionCut(menuBar);
}
void WorkbenchManipulator::modifyContextMenu(const char* recipient, Gui::MenuItem* menuBar)
{
if (strcmp(recipient, "View") == 0) {
addSelectionFilter(menuBar);
}
}
void WorkbenchManipulator::modifyToolBars(Gui::ToolBarItem* toolBar)
{
addSelectionFilter(toolBar);
@@ -78,12 +71,3 @@ void WorkbenchManipulator::addSelectionFilter(Gui::ToolBarItem* toolBar)
}
}
}
void WorkbenchManipulator::addSelectionFilter(Gui::MenuItem* menuBar)
{
if (auto measure = menuBar->findItem("Measure")) {
auto add = new Gui::MenuItem(); // NOLINT
add->setCommand("Part_SelectFilter");
menuBar->insertItem(measure, add);
}
}

View File

@@ -38,12 +38,6 @@ protected:
* The default implementation doesn't change anything.SectionCut
*/
void modifyMenuBar(Gui::MenuItem* menuBar) override;
/*!
* \brief modifyContextMenu
* Method to manipulate the contextmenu structure of a workbench.
* The default implementation doesn't change anything.
*/
void modifyContextMenu(const char* recipient, Gui::MenuItem* menuBar) override;
/*!
* \brief modifyToolBars
* Method to manipulate the toolbar structure of a workbench
@@ -60,7 +54,6 @@ protected:
private:
static void addSectionCut(Gui::MenuItem* menuBar);
static void addSelectionFilter(Gui::ToolBarItem* toolBar);
static void addSelectionFilter(Gui::MenuItem* menuBar);
};
} // namespace PartGui

View File

@@ -31,6 +31,7 @@
#include "DatumLine.h"
#include "DatumPlane.h"
#include "DatumPoint.h"
#include "Measure.h"
#include "FeatureBase.h"
#include "FeatureBoolean.h"
#include "FeatureChamfer.h"
@@ -153,5 +154,7 @@ PyMOD_INIT_FUNC(_PartDesign)
PartDesign::SubtractiveWedge ::init();
PartDesign::FeatureBase ::init();
PartDesign::Measure ::initialize();
PyMOD_Return(mod);
}

View File

@@ -117,6 +117,8 @@ SET(Module_SRCS
AppPartDesignPy.cpp
PreCompiled.cpp
PreCompiled.h
Measure.cpp
Measure.h
)
SOURCE_GROUP("Module" FILES ${Module_SRCS})

View File

@@ -0,0 +1,36 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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"
#include <App/Application.h>
#include <App/MeasureManager.h>
#include "Base/Console.h"
#include "Measure.h"
void PartDesign::Measure::initialize() {
const App::MeasureHandler& handler = App::MeasureManager::getMeasureHandler("Part");
App::MeasureManager::addMeasureHandler("PartDesign", handler.typeCb);
}

View File

@@ -0,0 +1,44 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* *
* 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 PARTDESIGN_MEASURE_H
#define PARTDESIGN_MEASURE_H
#include <Mod/PartDesign/PartDesignGlobal.h>
namespace PartDesign
{
class PartDesignExport Measure
{
public:
static void initialize();
};
} //namespace PartDesign
#endif

View File

@@ -580,20 +580,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const
*part << "Separator" << "PartDesign_WizardShaft";
}
// use Part's measure features also for PartDesign
Gui::MenuItem* measure = new Gui::MenuItem;
root->insertItem(item, measure);
measure->setCommand("Measure");
*measure << "Part_Measure_Linear"
<< "Part_Measure_Angular"
<< "Separator"
<< "Part_Measure_Refresh"
<< "Part_Measure_Clear_All"
<< "Part_Measure_Toggle_All"
<< "Part_Measure_Toggle_3D"
<< "Part_Measure_Toggle_Delta";
Gui::MenuItem* view = root->findItem("&View");
if (view) {
Gui::MenuItem* appr = view->findItem("Std_RandomColor");
@@ -662,19 +648,6 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Separator"
<< "PartDesign_Boolean";
// use Part's measure features also for PartDesign
Gui::ToolBarItem* measure = new Gui::ToolBarItem(root);
measure->setCommand("Measure");
*measure << "Part_Measure_Linear"
<< "Part_Measure_Angular"
<< "Separator"
<< "Part_Measure_Refresh"
<< "Part_Measure_Clear_All"
<< "Part_Measure_Toggle_All"
<< "Part_Measure_Toggle_3D"
<< "Part_Measure_Toggle_Delta";
return root;
}

View File

@@ -41,7 +41,7 @@
#include "SketchObjectSF.h"
#include "SketchPy.h"
#include "SolverGeometryExtension.h"
#include "Measure.h"
namespace Sketcher
{
@@ -95,6 +95,10 @@ PyMOD_INIT_FUNC(Sketcher)
Sketcher::Constraint ::init();
Sketcher::PropertyConstraintList ::init();
// connect to unified measurement facility
Sketcher::Measure ::initialize();
Base::Console().Log("Loading Sketcher module... done\n");
PyMOD_Return(sketcherModule);

View File

@@ -121,6 +121,8 @@ SET(SketchModule_SRCS
AppSketcherPy.cpp
PreCompiled.cpp
PreCompiled.h
Measure.cpp
Measure.h
)
SOURCE_GROUP("Module" FILES ${SketchModule_SRCS})

View File

@@ -0,0 +1,38 @@
/***************************************************************************
* Copyright (c) 2023 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 for establishing our connection with the unified measurement facility
//! we are treating sketches like Part objects for now
#include "PreCompiled.h"
#include <App/Application.h>
#include <App/MeasureManager.h>
#include "Base/Console.h"
#include "Measure.h"
void Sketcher::Measure::initialize()
{
const App::MeasureHandler& handler = App::MeasureManager::getMeasureHandler("Part");
App::MeasureManager::addMeasureHandler("Sketcher", handler.typeCb);
}

View File

@@ -0,0 +1,43 @@
/***************************************************************************
* Copyright (c) 2023 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 for establishing our connection with the unified measurement facility
#ifndef SKETCHER_MEASURE_H
#define SKETCHER_MEASURE_H
#include <Mod/Sketcher/SketcherGlobal.h>
namespace Sketcher
{
class SketcherExport Measure
{
public:
static void initialize();
};
} // namespace Sketcher
#endif