[Measure] Add features, viewproviders and preferences for unified measurement facility

This commit is contained in:
hlorus
2024-01-14 21:46:50 +01:00
committed by WandererFan
parent 309e1c4155
commit bed703a6f9
49 changed files with 5762 additions and 132 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

@@ -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,317 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
width="64px"
height="64px"
id="svg2943"
version="1.1">
<defs
id="defs2945">
<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
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>
<linearGradient
xlink:href="#linearGradient4052"
id="linearGradient4058"
x1="138.99986"
y1="44.863674"
x2="92.497559"
y2="-14.356517"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(168.6744,65.825928)" />
<linearGradient
xlink:href="#linearGradient4060"
id="linearGradient4066"
x1="103.93729"
y1="49.179436"
x2="120.49899"
y2="0.21229285"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(168.6744,65.825928)" />
<linearGradient
xlink:href="#linearGradient4122"
id="linearGradient4128"
x1="391.3074"
y1="120.81136"
x2="394.43201"
y2="112.43636"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-88.034794,-1.0606602)" />
<linearGradient
xlink:href="#linearGradient4158"
id="linearGradient4164"
x1="419.99387"
y1="102.77802"
x2="458.7193"
y2="69.431564"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-129.22376,-0.88388348)" />
<linearGradient
xlink:href="#linearGradient3953"
id="linearGradient3959"
x1="214.70918"
y1="80.886589"
x2="218.70918"
y2="104.88659"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
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
xlink:href="#linearGradient3961"
id="linearGradient3967"
x1="196.70918"
y1="106.88659"
x2="190.70918"
y2="80.886589"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
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>
</defs>
<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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1">
<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"
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.000002 2,36.000002 l 30,12 0,-6 -2,-4 c 8,-12 0,-24.000002 -4,-34.000002 z"
id="path3100" />
<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" />
<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" />
<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.000002 4,34.000002 l 12,-4 c 8,-12.000002 0,-24.000002 -4,-34.000002 z"
id="path3894" />
<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" />
<path
style="fill:url(#linearGradient3967);fill-opacity:1;stroke:#d3d7cf;stroke-width:1.99999976;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.469562 1.8,32.600002 l 26.2,10.4 0,-2.5 -2.2,-4.5 c 8,-12 1.2,-22.000002 -3.2,-32.100002 z"
id="path3100-6" />
<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" />
<path
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"
d="m -78,39 a 15,15 0 1 1 -30,0 15,15 0 1 1 30,0 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" />
<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" />
<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 293.70919,117.88659 8,-3 16,5 -6,4 z"
id="path4003" />
<path
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 317.70919,119.88659 0,4 -6,4 0,-4 z"
id="path4005" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,317 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
width="64px"
height="64px"
id="svg2943"
version="1.1">
<defs
id="defs2945">
<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
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>
<linearGradient
xlink:href="#linearGradient4052"
id="linearGradient4058"
x1="138.99986"
y1="44.863674"
x2="92.497559"
y2="-14.356517"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(168.6744,65.825928)" />
<linearGradient
xlink:href="#linearGradient4060"
id="linearGradient4066"
x1="103.93729"
y1="49.179436"
x2="120.49899"
y2="0.21229285"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(168.6744,65.825928)" />
<linearGradient
xlink:href="#linearGradient4122"
id="linearGradient4128"
x1="391.3074"
y1="120.81136"
x2="394.43201"
y2="112.43636"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-88.034794,-1.0606602)" />
<linearGradient
xlink:href="#linearGradient4158"
id="linearGradient4164"
x1="419.99387"
y1="102.77802"
x2="458.7193"
y2="69.431564"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-129.22376,-0.88388348)" />
<linearGradient
xlink:href="#linearGradient3953"
id="linearGradient3959"
x1="214.70918"
y1="80.886589"
x2="218.70918"
y2="104.88659"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
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
xlink:href="#linearGradient3961"
id="linearGradient3967"
x1="196.70918"
y1="106.88659"
x2="190.70918"
y2="80.886589"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(80,0)" />
<linearGradient
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>
</defs>
<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>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1">
<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"
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.000002 2,36.000002 l 30,12 0,-6 -2,-4 c 8,-12 0,-24.000002 -4,-34.000002 z"
id="path3100" />
<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" />
<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" />
<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.000002 4,34.000002 l 12,-4 c 8,-12.000002 0,-24.000002 -4,-34.000002 z"
id="path3894" />
<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" />
<path
style="fill:url(#linearGradient3967);fill-opacity:1;stroke:#d3d7cf;stroke-width:1.99999976;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.469562 1.8,32.600002 l 26.2,10.4 0,-2.5 -2.2,-4.5 c 8,-12 1.2,-22.000002 -3.2,-32.100002 z"
id="path3100-6" />
<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" />
<path
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"
d="m -78,39 a 15,15 0 1 1 -30,0 15,15 0 1 1 30,0 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" />
<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" />
<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 293.70919,117.88659 8,-3 16,5 -6,4 z"
id="path4003" />
<path
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 317.70919,119.88659 0,4 -6,4 0,-4 z"
id="path4005" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 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

@@ -1,24 +1,25 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
* 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 the FreeCAD CAx development system. *
* This file is part of FreeCAD. *
* *
* 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. *
* 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. *
* *
* 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. *
* 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 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 *
* 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"
@@ -39,11 +40,14 @@
#include <GProp_GProps.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <gp_Circ.hxx>
#include <DatumFeature.h>
#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>
@@ -59,7 +63,9 @@
#include "VectorAdapter.h"
#include "PartFeature.h"
#include "Measure.h"
#include "MeasureClient.h"
using namespace Part;
// From: https://github.com/Celemation/FreeCAD/blob/joel_selection_summary_demo/src/Gui/SelectionSummary.cpp
@@ -90,11 +96,16 @@ static float getRadius(TopoDS_Shape& edge){
return 0.0;
}
TopoDS_Shape getLocatedShape(const std::string& objectName, const std::string& subName)
TopoDS_Shape getLocatedShape(const App::SubObjectT& subject)
{
App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(objectName.c_str());
// should this be getTopoShape(obj, subName, true)?
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());
@@ -105,22 +116,19 @@ TopoDS_Shape getLocatedShape(const std::string& objectName, const std::string& s
return shape.getShape();
}
if (subName.empty())
{
if (!subject.getElementName()) {
return shape.getShape();
}
return shape.getSubShape(subName.c_str());
return shape.getSubShape(subject.getElementName(), true);
}
App::MeasureElementType PartMeasureTypeCb(const char* objectName, const char* subName) {
App::DocumentObject* ob = App::GetApplication().getActiveDocument()->getObject(objectName);
// auto sub = ob->getSubObject(subName);
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::VectorAdapter did not retrieve shape for %s, %s\n", objectName, subName);
Base::Console().Message("Part::PartMeasureTypeCb did not retrieve shape for %s, %s\n", ob->getNameInDocument(), subName);
return App::MeasureElementType();
}
TopAbs_ShapeEnum shapeType = shape.ShapeType();
@@ -168,31 +176,29 @@ App::MeasureElementType PartMeasureTypeCb(const char* objectName, const char* su
}
bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub, Base::Matrix4D *mat)
bool getShapeFromStrings(TopoDS_Shape &shapeOut, const App::SubObjectT& subject, Base::Matrix4D *mat)
{
App::Document *docPointer = App::GetApplication().getDocument(doc.c_str());
if (!docPointer) {
return false;
}
App::DocumentObject *objectPointer = docPointer->getObject(object.c_str());
if (!objectPointer) {
return false;
}
shapeOut = Part::Feature::getShape(objectPointer,sub.c_str(),true,mat);
return !shapeOut.IsNull();
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::DocumentObject* ob, std::string* objectName, const std::string* subName)
Part::VectorAdapter buildAdapter(const App::SubObjectT& subject)
{
(void) objectName;
if (!subject.getObject()) {
return Part::VectorAdapter();
}
Base::Matrix4D mat;
TopoDS_Shape shape = Part::Feature::getShape(ob, subName->c_str(), true);
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::VectorAdapter did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str());
Base::Console().Message("Part::buildAdapter did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return Part::VectorAdapter();
}
@@ -202,7 +208,7 @@ Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* obj
if (shapeType == TopAbs_EDGE)
{
TopoDS_Shape edgeShape;
if (!getShapeFromStrings(edgeShape, ob->getDocument()->getName(), ob->getNameInDocument(), *subName, &mat)) {
if (!getShapeFromStrings(edgeShape, subject, &mat)) {
return {};
}
TopoDS_Edge edge = TopoDS::Edge(edgeShape);
@@ -233,7 +239,7 @@ Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* obj
if (shapeType == TopAbs_FACE)
{
TopoDS_Shape faceShape;
if (!getShapeFromStrings(faceShape, ob->getDocument()->getName(), ob->getNameInDocument(), *subName, &mat)) {
if (!getShapeFromStrings(faceShape, subject, &mat)) {
return {};
}
@@ -248,18 +254,20 @@ Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* obj
}
Measure::MeasureLengthInfo MeasureLengthHandler(std::string* objectName, std::string* subName){
TopoDS_Shape shape = getLocatedShape(*objectName, *subName);
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", objectName->c_str(), subName->c_str());
return {false, 0.0, Base::Matrix4D()};
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 {false, 0.0, Base::Matrix4D()};
return std::make_shared<MeasureLengthInfo>(false, 0.0, Base::Matrix4D());
}
// Get Center of mass as the attachment point of the label
@@ -278,22 +286,23 @@ Measure::MeasureLengthInfo MeasureLengthHandler(std::string* objectName, std::st
Base::Rotation rot(axisUp, elementDirection);
Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot);
return {true, getLength(shape), placement};
return std::make_shared<MeasureLengthInfo>(true, getLength(shape), placement);
}
Measure::MeasureRadiusInfo MeasureRadiusHandler(std::string* objectName, std::string* subName){
MeasureRadiusInfoPtr MeasureRadiusHandler(const App::SubObjectT& subject)
{
Base::Placement placement; // curve center + orientation
Base::Vector3d pointOnCurve;
TopoDS_Shape shape = getLocatedShape(*objectName, *subName);
TopoDS_Shape shape = getLocatedShape(subject);
if (shape.IsNull()) {
return { false, 0.0, pointOnCurve, placement};
return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
}
TopAbs_ShapeEnum sType = shape.ShapeType();
if (sType != TopAbs_EDGE) {
return { false, 0.0, pointOnCurve, placement};
return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
}
// Get Center of mass as the attachment point of the label
@@ -312,22 +321,24 @@ Measure::MeasureRadiusInfo MeasureRadiusHandler(std::string* objectName, std::st
placement = Base::Placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot);
return { true, getRadius(shape), pointOnCurve, placement};
return std::make_shared<MeasureRadiusInfo>( true, getRadius(shape), pointOnCurve, placement);
}
Measure::MeasureAreaInfo MeasureAreaHandler(std::string* objectName, std::string* subName){
TopoDS_Shape shape = getLocatedShape(*objectName, *subName);
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", objectName->c_str(), subName->c_str());
return {false, 0.0, Base::Matrix4D()};
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 {false, 0.0, Base::Matrix4D()};
return std::make_shared<MeasureAreaInfo>(false, 0.0, Base::Matrix4D());
}
// Get Center of mass as the attachment point of the label
@@ -338,41 +349,45 @@ Measure::MeasureAreaInfo MeasureAreaHandler(std::string* objectName, std::string
// TODO: Center of Mass might not lie on the surface, somehow snap to the closest point on the surface?
Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation());
return {true, getFaceArea(shape), placement};
return std::make_shared<MeasureAreaInfo>(true, getFaceArea(shape), placement);
}
Base::Vector3d MeasurePositionHandler(std::string* objectName, std::string* subName) {
TopoDS_Shape shape = getLocatedShape(*objectName, *subName);
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", objectName->c_str(), subName->c_str());
return Base::Vector3d();
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 Base::Vector3d();
return std::make_shared<MeasurePositionInfo>(false, Base::Vector3d());
}
TopoDS_Vertex vertex = TopoDS::Vertex(shape);
auto point = BRep_Tool::Pnt(vertex);
return Base::Vector3d(point.X(), point.Y(), point.Z());
return std::make_shared<MeasurePositionInfo>( true, Base::Vector3d(point.X(), point.Y(), point.Z()));
}
Measure::MeasureAngleInfo MeasureAngleHandler(std::string* objectName, std::string* subName) {
App::DocumentObject* ob = App::GetApplication().getActiveDocument()->getObject(objectName->c_str());
TopoDS_Shape shape = getLocatedShape(*objectName, *subName);
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", objectName->c_str(), subName->c_str());
return Measure::MeasureAngleInfo();
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(ob, objectName, subName);
Part::VectorAdapter vAdapt = buildAdapter(subject);
gp_Pnt vec;
Base::Vector3d position;
@@ -393,48 +408,89 @@ Measure::MeasureAngleInfo MeasureAngleHandler(std::string* objectName, std::stri
position.Set(vec.X(), vec.Y(), vec.Z());
Measure::MeasureAngleInfo info = {vAdapt.isValid(), (Base::Vector3d)vAdapt, position};
auto info = std::make_shared<MeasureAngleInfo>(vAdapt.isValid(), (Base::Vector3d)vAdapt, position);
return info;
}
Measure::MeasureDistanceInfo MeasureDistanceHandler(std::string* objectName, std::string* subName) {
TopoDS_Shape shape = getLocatedShape(*objectName, *subName);
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", objectName->c_str(), subName->c_str());
return Measure::MeasureDistanceInfo();
Base::Console().Message("MeasureDistanceHandler did not retrieve shape for %s, %s\n",
subject.getObjectName(), subject.getElementName());
return std::make_shared<MeasureDistanceInfo>();
}
// Just return the TopoDS_Shape here
return {true, shape};
// 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);
}
using namespace Measure;
void Part::MeasureClient::initialize() {
App::MeasureManager::addMeasureHandler("Part", PartMeasureTypeCb);
void Part::Measure::initialize() {
App::Application& app = App::GetApplication();
app.addMeasureHandler("Part", PartMeasureTypeCb);
std::vector<std::string> proxyList( { "Part", "PartDesign", "Sketcher" } );
// Extend MeasureLength
MeasureLength::addGeometryHandlers(proxyList, MeasureLengthHandler);
// Extend MeasurePosition
MeasurePosition::addGeometryHandlers(proxyList, MeasurePositionHandler);
// Extend MeasureArea
MeasureArea::addGeometryHandlers(proxyList, MeasureAreaHandler);
// Extend MeasureAngle
MeasureAngle::addGeometryHandlers(proxyList, MeasureAngleHandler);
// Extend MeasureDistance
MeasureDistance::addGeometryHandlers(proxyList, MeasureDistanceHandler);
// Extend MeasureRadius
MeasureRadius::addGeometryHandlers(proxyList, MeasureRadiusHandler);
}
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;
}