/*************************************************************************** * Copyright (c) 2023 David Friedli * * * * 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 * * . * * * **************************************************************************/ #include "PreCompiled.h" #include #include #include #include #include #include #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(DistanceX,(0.0),"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Distance in X direction"); DistanceX.setUnit(Base::Unit::Length); ADD_PROPERTY_TYPE(DistanceY,(0.0),"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Distance in Y direction"); DistanceY.setUnit(Base::Unit::Length); ADD_PROPERTY_TYPE(DistanceZ,(0.0),"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Distance in Z direction"); DistanceZ.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 type = App::MeasureManager::getMeasureElementType(element); 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 elems1 = {objT1.getSubName()}; Element1.setValue(ob1, elems1); auto element2 = selection.at(1); auto objT2 = element2.object; App::DocumentObject* ob2 = objT2.getObject(); const std::vector elems2 = {objT2.getSubName()}; Element2.setValue(ob2, elems2); } bool MeasureDistance::getShape(App::PropertyLinkSub* prop, TopoDS_Shape& rShape) { App::DocumentObject* ob = prop->getValue(); std::vector subs = prop->getSubValues(); if (!ob || !ob->isValid() || subs.empty() ) { return false; } std::string subName = subs.at(0); App::SubObjectT subject{ob, subName.c_str()}; auto info = getMeasureInfo(subject); if (!info || !info->valid) { return false; } auto distanceInfo = std::dynamic_pointer_cast(info); rShape = distanceInfo->getShape(); return true; } App::DocumentObjectExecReturn *MeasureDistance::execute() { App::DocumentObject* ob1 = Element1.getValue(); std::vector subs1 = Element1.getSubValues(); App::DocumentObject* ob2 = Element2.getValue(); std::vector 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"); } gp_Pnt delta = measure.PointOnShape2(1).XYZ() - measure.PointOnShape1(1).XYZ(); Distance.setValue(measure.Value()); DistanceX.setValue(fabs(delta.X())); DistanceY.setValue(fabs(delta.Y())); DistanceZ.setValue(fabs(delta.Z())); 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 MeasureDistance::getSubject() const { return {Element1.getValue()}; } PROPERTY_SOURCE(Measure::MeasureDistanceDetached, Measure::MeasureBase) MeasureDistanceDetached::MeasureDistanceDetached() { 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(DistanceX,(0.0),"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Distance in X direction"); DistanceX.setUnit(Base::Unit::Length); ADD_PROPERTY_TYPE(DistanceY,(0.0),"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Distance in Y direction"); DistanceY.setUnit(Base::Unit::Length); ADD_PROPERTY_TYPE(DistanceZ,(0.0),"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), "Distance in Z direction"); DistanceZ.setUnit(Base::Unit::Length); ADD_PROPERTY_TYPE(Position1,(Base::Vector3d(0.0,0.0,0.0)),"Measurement", App::Prop_None, "Position1"); ADD_PROPERTY_TYPE(Position2,(Base::Vector3d(0.0,1.0,0.0)),"Measurement", App::Prop_None, "Position2"); } MeasureDistanceDetached::~MeasureDistanceDetached() = default; bool MeasureDistanceDetached::isValidSelection(const App::MeasureSelection& selection){ return selection.size() == 2; } void MeasureDistanceDetached::parseSelection(const App::MeasureSelection& selection) { auto sel1 = selection.at(0); auto sel2 = selection.at(1); Position1.setValue(sel1.pickedPoint); Position2.setValue(sel2.pickedPoint); } App::DocumentObjectExecReturn *MeasureDistanceDetached::execute() { recalculateDistance(); return DocumentObject::StdReturn; } void MeasureDistanceDetached::recalculateDistance() { auto delta = Position1.getValue() - Position2.getValue(); Distance.setValue(delta.Length()); DistanceX.setValue(fabs(delta.x)); DistanceY.setValue(fabs(delta.y)); DistanceZ.setValue(fabs(delta.z)); } void MeasureDistanceDetached::onChanged(const App::Property* prop) { if (isRestoring() || isRemoving()) { return; } if (prop == &Position1 || prop == &Position2) { recalculateDistance(); } MeasureBase::onChanged(prop); } std::vector MeasureDistanceDetached::getSubject() const { return {}; } Base::Type MeasureDistanceType::getClassTypeId() { return Base::Type::badType(); } Base::Type MeasureDistanceType::getTypeId() const { return Base::Type::badType(); } void MeasureDistanceType::init() { initSubclass(MeasureDistanceType::classTypeId, "App::MeasureDistance", "App::DocumentObject", &(MeasureDistanceType::create)); } void* MeasureDistanceType::create() { return new MeasureDistanceDetached(); } Base::Type MeasureDistanceType::classTypeId = Base::Type::badType(); // Migrate old MeasureDistance Type void MeasureDistanceDetached::handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName) { if (strcmp(PropName, "P1") == 0 && strcmp(TypeName, "App::PropertyVector") == 0) { Position1.Restore(reader); } else if (strcmp(PropName, "P2") == 0 && strcmp(TypeName, "App::PropertyVector") == 0) { Position2.Restore(reader); } }