Merge pull request #18332 from Ondsel-Development/core_LCS3

Core Datums: Card4 : implement part subclasses.
This commit is contained in:
Chris Hennes
2024-12-13 15:59:10 -05:00
committed by GitHub
44 changed files with 1326 additions and 581 deletions

View File

@@ -3,22 +3,21 @@
* Copyright (c) 2015 Alexander Golubev (Fat-Zer) <fatzer2@gmail.com> *
* Copyright (c) 2024 Ondsel (PL Boyer) <development@ondsel.com> *
* *
* 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/>. *
* *
***************************************************************************/
@@ -207,9 +206,9 @@ const std::vector<LocalCoordinateSystem::SetupData>& LocalCoordinateSystem::getS
{
static const std::vector<SetupData> setupData = {
// clang-format off
{App::Line::getClassTypeId(), AxisRoles[0], tr("X-axis"), Base::Rotation()},
{App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)},
{App::Line::getClassTypeId(), AxisRoles[2], tr("Z-axis"), Base::Rotation(Base::Vector3d(1,-1, 1), M_PI * 2 / 3)},
{App::Line::getClassTypeId(), AxisRoles[0], tr("X-axis"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)},
{App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(-1, 1, 1), M_PI * 2 / 3)},
{App::Line::getClassTypeId(), AxisRoles[2], tr("Z-axis"), Base::Rotation()},
{App::Plane::getClassTypeId(), PlaneRoles[0], tr("XY-plane"), Base::Rotation()},
{App::Plane::getClassTypeId(), PlaneRoles[1], tr("XZ-plane"), Base::Rotation(1.0, 0.0, 0.0, 1.0)},
{App::Plane::getClassTypeId(), PlaneRoles[2], tr("YZ-plane"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)},
@@ -277,6 +276,56 @@ void LocalCoordinateSystem::unsetupObject()
}
}
void LocalCoordinateSystem::onDocumentRestored()
{
GeoFeature::onDocumentRestored();
// In 0.22 origins did not have point.
migrateOriginPoint();
// In 0.22 the axis placement were wrong. The X axis had identity placement instead of the Z.
// This was fixed but we need to migrate old files.
migrateXAxisPlacement();
}
void LocalCoordinateSystem::migrateOriginPoint()
{
auto features = OriginFeatures.getValues();
auto isOrigin = [](App::DocumentObject* obj) {
return obj->isDerivedFrom<App::DatumElement>() &&
strcmp(static_cast<App::DatumElement*>(obj)->Role.getValue(), PointRoles[0]) == 0;
};
if (std::none_of(features.begin(), features.end(), isOrigin)) {
auto data = getData(PointRoles[0]);
auto* origin = createDatum(data);
features.push_back(origin);
OriginFeatures.setValues(features);
}
}
void LocalCoordinateSystem::migrateXAxisPlacement()
{
auto features = OriginFeatures.getValues();
migrated = false;
const auto& setupData = getSetupData();
for (auto* obj : features) {
auto* feature = dynamic_cast <App::DatumElement*> (obj);
if (!feature) { continue; }
for (auto data : setupData) {
// ensure the rotation is correct for the role
if (std::strcmp(feature->Role.getValue(), data.role) == 0) {
if (!feature->Placement.getValue().getRotation().isSame(data.rot)) {
feature->Placement.setValue(Base::Placement(Base::Vector3d(), data.rot));
migrated = true;
}
}
}
}
}
// ----------------------------------------------------------------------------
LocalCoordinateSystem::LCSExtension::LCSExtension(LocalCoordinateSystem* obj)

View File

@@ -3,22 +3,21 @@
* Copyright (c) 2015 Alexander Golubev (Fat-Zer) <fatzer2@gmail.com> *
* Copyright (c) 2024 Ondsel (PL Boyer) <development@ondsel.com> *
* *
* 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/>. *
* *
***************************************************************************/
@@ -206,6 +205,8 @@ public:
// Axis links
PropertyLinkList OriginFeatures;
bool migrated;
protected:
/// Checks integrity of the LCS
App::DocumentObjectExecReturn* execute() override;
@@ -213,6 +214,7 @@ protected:
void setupObject() override;
/// Removes all planes and axis if they are still linked to the document
void unsetupObject() override;
void onDocumentRestored() override;
private:
struct SetupData;
@@ -245,6 +247,9 @@ private:
DatumElement* createDatum(SetupData& data);
SetupData getData(const char* role);
void migrateOriginPoint();
void migrateXAxisPlacement();
};
} // namespace App

View File

@@ -134,7 +134,7 @@ ElementNamePair GeoFeature::_getElementName(const char* name,
}
}
DocumentObject* GeoFeature::resolveElement(DocumentObject* obj,
DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj,
const char* subname,
ElementNamePair& elementName,
bool append,

View File

@@ -104,7 +104,7 @@ public:
*
* @return Return the owner object of the element
*/
static DocumentObject* resolveElement(App::DocumentObject* obj,
static DocumentObject* resolveElement(const App::DocumentObject* obj,
const char* subname,
ElementNamePair& elementName,
bool append = false,

View File

@@ -156,9 +156,9 @@ void StdCmdVarSet::activated(int iMsg)
// add the varset to a group if it is selected
auto sels = Selection().getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(),
ResolveMode::OldStyleElement, true);
ResolveMode::OldStyleElement, true);
if (sels.size() == 1) {
App::DocumentObject *obj = sels[0].getObject();
App::DocumentObject* obj = sels[0].getObject();
auto group = obj->getExtension<App::GroupExtension>();
if (group) {
Gui::Document* docGui = Application::Instance->activeDocument();

View File

@@ -26,6 +26,8 @@
#ifndef _PreComp_
# include <Inventor/nodes/SoLightModel.h>
# include <Inventor/nodes/SoSeparator.h>
# include <QMessageBox>
# include <QCheckBox>
#endif
#include <App/Document.h>
@@ -83,6 +85,50 @@ void ViewProviderCoordinateSystem::attach(App::DocumentObject* pcObject)
addDisplayMaskMode(pcGroupChildren, "Base");
}
void ViewProviderCoordinateSystem::finishRestoring()
{
showMigrationDialog();
}
void ViewProviderCoordinateSystem::showMigrationDialog()
{
auto lcs = dynamic_cast<App::LocalCoordinateSystem*>(getObject());
if (!lcs || !lcs->migrated) {
return;
}
static bool userWarned = false;
if (userWarned || !App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/View")->GetBool("ShowLCSMigrationWarning", true)) {
return;
}
// Display the warning message
QMessageBox msgBox(QMessageBox::Warning,
QObject::tr("File Migration Warning"),
QObject::tr("This file was created with an older version of FreeCAD. "
"Origin axes had incorrect placements, which have now been corrected.\n\n"
"However, if you save this file in the current version and reopen it in an"
" older version of FreeCAD, the origin axes will be misaligned. Additionally, "
"if your file references these origin axes, your file will likely be broken."),
QMessageBox::Ok);
QCheckBox* checkBox = new QCheckBox(QObject::tr("Don't show this warning again"));
msgBox.setCheckBox(checkBox);
msgBox.exec();
// Update static flag if the user has seen the warning
userWarned = true;
// Save preference if the user selects "Don't show again"
if (checkBox->isChecked()) {
App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/View")->SetBool("ShowLCSMigrationWarning", false);
}
}
std::vector<std::string> ViewProviderCoordinateSystem::getDisplayModes() const
{
return { "Base" };

View File

@@ -83,7 +83,11 @@ protected:
void updateData(const App::Property*) override;
bool onDelete(const std::vector<std::string> &) override;
void finishRestoring() override;
private:
void showMigrationDialog();
SoGroup *pcGroupChildren;
std::map<Gui::ViewProvider*, bool> tempVisMap;

View File

@@ -83,12 +83,12 @@ void ViewProviderLine::attach(App::DocumentObject *obj) {
SbVec3f verts[2];
if (noRole) {
verts[0] = SbVec3f(2 * size, 0, 0);
verts[0] = SbVec3f(0, 0, 2 * size);
verts[1] = SbVec3f(0, 0, 0);
}
else {
verts[0] = SbVec3f(size, 0, 0);
verts[1] = SbVec3f(0.2 * size, 0, 0);
verts[0] = SbVec3f(0, 0, size);
verts[1] = SbVec3f(0, 0, 0.2 * size);
}
// indexes used to create the edges
@@ -107,7 +107,7 @@ void ViewProviderLine::attach(App::DocumentObject *obj) {
sep->addChild ( pLines );
auto textTranslation = new SoTranslation ();
textTranslation->translation.setValue ( SbVec3f ( size * 1.1, 0, 0 ) );
textTranslation->translation.setValue(SbVec3f(0, 0, size * 1.1));
sep->addChild ( textTranslation );
auto ps = new SoPickStyle();

View File

@@ -33,6 +33,7 @@
#include <chrono>
#include <App/Application.h>
#include <App/Datums.h>
#include <App/Document.h>
#include <App/DocumentObjectGroup.h>
#include <App/FeaturePythonPyImp.h>
@@ -45,6 +46,7 @@
#include <Base/Interpreter.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/Part/App/AttachExtension.h>
#include <OndselSolver/CREATE.h>
#include <OndselSolver/ASMTSimulationParameters.h>
@@ -93,6 +95,10 @@ FC_LOG_LEVEL_INIT("Assembly", true, true, true)
using namespace Assembly;
using namespace MbD;
namespace PartApp = Part;
// ================================ Assembly Object ============================
PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part)
@@ -135,7 +141,7 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS)
objectPartMap.clear();
motions.clear();
std::vector<App::DocumentObject*> groundedObjs = fixGroundedParts();
auto groundedObjs = fixGroundedParts();
if (groundedObjs.empty()) {
// If no part fixed we can't solve.
return -6;
@@ -178,7 +184,7 @@ int AssemblyObject::generateSimulation(App::DocumentObject* sim)
motions = getMotionsFromSimulation(sim);
std::vector<App::DocumentObject*> groundedObjs = fixGroundedParts();
auto groundedObjs = fixGroundedParts();
if (groundedObjs.empty()) {
// If no part fixed we can't solve.
return -6;
@@ -351,7 +357,8 @@ Base::Placement AssemblyObject::getMbdPlacement(std::shared_ptr<ASMTPart> mbdPar
bool AssemblyObject::validateNewPlacements()
{
// First we check if a grounded object has moved. It can happen that they flip.
for (auto* obj : getGroundedParts()) {
auto groundedParts = getGroundedParts();
for (auto* obj : groundedParts) {
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
if (propPlacement) {
@@ -367,7 +374,8 @@ bool AssemblyObject::validateNewPlacements()
if (!oldPlc.isSame(newPlacement)) {
Base::Console().Warning(
"Assembly : Ignoring bad solve, a grounded object moved.\n");
"Assembly : Ignoring bad solve, a grounded object (%s) moved.\n",
obj->getFullLabel());
return false;
}
}
@@ -749,11 +757,11 @@ std::vector<App::DocumentObject*> AssemblyObject::getJointsOfPart(App::DocumentO
return jointsOf;
}
std::vector<App::DocumentObject*> AssemblyObject::getGroundedParts()
std::unordered_set<App::DocumentObject*> AssemblyObject::getGroundedParts()
{
std::vector<App::DocumentObject*> groundedJoints = getGroundedJoints();
std::vector<App::DocumentObject*> groundedObjs;
std::unordered_set<App::DocumentObject*> groundedSet;
for (auto gJoint : groundedJoints) {
if (!gJoint) {
continue;
@@ -764,34 +772,49 @@ std::vector<App::DocumentObject*> AssemblyObject::getGroundedParts()
if (propObj) {
App::DocumentObject* objToGround = propObj->getValue();
groundedObjs.push_back(objToGround);
if (objToGround) {
groundedSet.insert(objToGround);
}
}
}
return groundedObjs;
// We also need to add all the root-level datums objects that are not attached.
std::vector<App::DocumentObject*> objs = Group.getValues();
for (auto* obj : objs) {
if (obj->isDerivedFrom<App::LocalCoordinateSystem>()
|| obj->isDerivedFrom<App::DatumElement>()) {
auto* pcAttach = obj->getExtensionByType<PartApp::AttachExtension>();
if (pcAttach) {
// If it's a Part datums, we check if it's attached. If yes then we ignore it.
std::string mode = pcAttach->MapMode.getValueAsString();
if (mode != "Deactivated") {
continue;
}
}
groundedSet.insert(obj);
}
}
// Origin is not in Group so we add it separately
groundedSet.insert(Origin.getValue());
return groundedSet;
}
std::vector<App::DocumentObject*> AssemblyObject::fixGroundedParts()
std::unordered_set<App::DocumentObject*> AssemblyObject::fixGroundedParts()
{
std::vector<App::DocumentObject*> groundedJoints = getGroundedJoints();
auto groundedParts = getGroundedParts();
std::vector<App::DocumentObject*> groundedObjs;
for (auto obj : groundedJoints) {
for (auto obj : groundedParts) {
if (!obj) {
continue;
}
auto* propObj = dynamic_cast<App::PropertyLink*>(obj->getPropertyByName("ObjectToGround"));
if (propObj) {
App::DocumentObject* objToGround = propObj->getValue();
Base::Placement plc = getPlacementFromProp(obj, "Placement");
std::string str = obj->getFullName();
fixGroundedPart(objToGround, plc, str);
groundedObjs.push_back(objToGround);
}
Base::Placement plc = getPlacementFromProp(obj, "Placement");
std::string str = obj->getFullName();
fixGroundedPart(obj, plc, str);
}
return groundedObjs;
return groundedParts;
}
void AssemblyObject::fixGroundedPart(App::DocumentObject* obj,
@@ -902,7 +925,7 @@ bool AssemblyObject::isObjInSetOfObjRefs(App::DocumentObject* obj, const std::ve
}
void AssemblyObject::removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs)
std::unordered_set<App::DocumentObject*> groundedObjs)
{
std::vector<ObjRef> connectedParts;
@@ -994,7 +1017,7 @@ bool AssemblyObject::isPartGrounded(App::DocumentObject* obj)
return false;
}
std::vector<App::DocumentObject*> groundedObjs = getGroundedParts();
auto groundedObjs = getGroundedParts();
for (auto* groundedObj : groundedObjs) {
if (groundedObj->getFullName() == obj->getFullName()) {
@@ -1011,7 +1034,7 @@ bool AssemblyObject::isPartConnected(App::DocumentObject* obj)
return false;
}
std::vector<App::DocumentObject*> groundedObjs = getGroundedParts();
auto groundedObjs = getGroundedParts();
std::vector<App::DocumentObject*> joints = getJoints(false);
std::vector<ObjRef> connectedParts;
@@ -1958,29 +1981,6 @@ std::vector<AssemblyLink*> AssemblyObject::getSubAssemblies()
return subAssemblies;
}
void AssemblyObject::updateGroundedJointsPlacements()
{
std::vector<App::DocumentObject*> groundedJoints = getGroundedJoints();
for (auto gJoint : groundedJoints) {
if (!gJoint) {
continue;
}
auto* propObj =
dynamic_cast<App::PropertyLink*>(gJoint->getPropertyByName("ObjectToGround"));
auto* propPlc =
dynamic_cast<App::PropertyPlacement*>(gJoint->getPropertyByName("Placement"));
if (propObj && propPlc) {
App::DocumentObject* obj = propObj->getValue();
auto* propObjPlc =
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
propPlc->setValue(propObjPlc->getValue());
}
}
}
void AssemblyObject::ensureIdentityPlacements()
{
std::vector<App::DocumentObject*> group = Group.getValues();
@@ -2011,36 +2011,3 @@ void AssemblyObject::ensureIdentityPlacements()
}
}
}
/*void Part::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property*
prop)
{
App::Part::handleChangedPropertyType(reader, TypeName, prop);
}*/
/* Apparently not necessary as App::Part doesn't have this.
// Python Assembly feature ---------------------------------------------------------
namespace App
{
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(Assembly::AssemblyObjectPython, Assembly::AssemblyObject)
template<>
const char* Assembly::AssemblyObjectPython::getViewProviderName() const
{
return "AssemblyGui::ViewProviderAssembly";
}
template<>
PyObject* Assembly::AssemblyObjectPython::getPyObject()
{
if (PythonObject.is(Py::_None())) {
// ref counter is set to 1
PythonObject = Py::Object(new FeaturePythonPyT<AssemblyObjectPy>(this), true);
}
return Py::new_reference_to(PythonObject);
}
/// @endcond
// explicit template instantiation
template class AssemblyExport FeaturePythonT<Assembly::AssemblyObject>;
}// namespace App*/

View File

@@ -157,8 +157,8 @@ public:
std::vector<App::DocumentObject*> getJointsOfPart(App::DocumentObject* part);
App::DocumentObject* getJointOfPartConnectingToGround(App::DocumentObject* part,
std::string& name);
std::vector<App::DocumentObject*> getGroundedParts();
std::vector<App::DocumentObject*> fixGroundedParts();
std::unordered_set<App::DocumentObject*> getGroundedParts();
std::unordered_set<App::DocumentObject*> fixGroundedParts();
void fixGroundedPart(App::DocumentObject* obj, Base::Placement& plc, std::string& jointName);
bool isJointConnectingPartToGround(App::DocumentObject* joint, const char* partPropName);
@@ -166,7 +166,7 @@ public:
bool isObjInSetOfObjRefs(App::DocumentObject* obj, const std::vector<ObjRef>& pairs);
void removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs);
std::unordered_set<App::DocumentObject*> groundedObjs);
void traverseAndMarkConnectedParts(App::DocumentObject* currentPart,
std::vector<ObjRef>& connectedParts,
const std::vector<App::DocumentObject*>& joints);
@@ -186,7 +186,6 @@ public:
void setObjMasses(std::vector<std::pair<App::DocumentObject*, double>> objectMasses);
std::vector<AssemblyLink*> getSubAssemblies();
void updateGroundedJointsPlacements();
std::vector<App::DocumentObject*> getMotionsFromSimulation(App::DocumentObject* sim);

View File

@@ -33,6 +33,7 @@
#endif
#include <App/Application.h>
#include <App/Datums.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/PropertyStandard.h>
@@ -536,7 +537,9 @@ App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, const std::st
// getViewProviderName instead of isDerivedFrom to avoid dependency on sketcher
const auto isDerivedFromVpSketch =
strcmp(obj->getViewProviderName(), "SketcherGui::ViewProviderSketch") == 0;
return isDerivedFromVpSketch || obj->isDerivedFrom<PartApp::Datum>();
return isDerivedFromVpSketch || obj->isDerivedFrom<PartApp::Datum>()
|| obj->isDerivedFrom<App::DatumElement>()
|| obj->isDerivedFrom<App::LocalCoordinateSystem>();
};
// Helper function to handle PartDesign::Body objects

View File

@@ -47,11 +47,7 @@ def noOtherTaskActive():
def isCreateJointActive():
return (
UtilsAssembly.isAssemblyGrounded()
and UtilsAssembly.assembly_has_at_least_n_parts(2)
and noOtherTaskActive()
)
return UtilsAssembly.assembly_has_at_least_n_parts(1) and noOtherTaskActive()
def activateJoint(index):

View File

@@ -229,12 +229,6 @@ bool ViewProviderAssembly::setEdit(int mode)
PARTKEY,
this->getObject()->getNameInDocument());
// When we set edit, we update the grounded joints placements to support :
// - If user transformed the grounded object
// - For nested assemblies where the grounded object moves around.
auto* assembly = getObject<AssemblyObject>();
assembly->updateGroundedJointsPlacements();
setDragger();
attachSelection();

View File

@@ -956,18 +956,6 @@ class GroundedJoint:
joint.ObjectToGround = obj_to_ground
joint.addProperty(
"App::PropertyPlacement",
"Placement",
"Ground",
QT_TRANSLATE_NOOP(
"App::Property",
"This is where the part is grounded.",
),
)
joint.Placement = obj_to_ground.Placement
def dumps(self):
return None
@@ -1160,24 +1148,33 @@ class MakeJointSelGate:
return False
ref = [obj, [sub]]
selected_object = UtilsAssembly.getObject(ref)
sel_obj = UtilsAssembly.getObject(ref)
if not (
selected_object.isDerivedFrom("Part::Feature")
or selected_object.isDerivedFrom("App::Part")
if UtilsAssembly.isLink(sel_obj):
linked = sel_obj.getLinkedObject()
if linked == sel_obj:
return True # We accept empty links
sel_obj = linked
if sel_obj.isDerivedFrom("Part::Feature") or sel_obj.isDerivedFrom("App::Part"):
return True
if sel_obj.isDerivedFrom("App::LocalCoordinateSystem") or sel_obj.isDerivedFrom(
"App::DatumElement"
):
if UtilsAssembly.isLink(selected_object):
linked = selected_object.getLinkedObject()
if linked == selected_object:
# We accept empty links
return True
datum = sel_obj
if datum.isDerivedFrom("App::DatumElement"):
parent = datum.getParent()
if parent.isDerivedFrom("App::LocalCoordinateSystem"):
datum = parent
if not (linked.isDerivedFrom("Part::Feature") or linked.isDerivedFrom("App::Part")):
return False
else:
return False
if self.assembly.hasObject(datum) and hasattr(datum, "MapMode"):
# accept only datum that are not attached
return datum.MapMode == "Deactivated"
return True
return True
return False
activeTask = None

View File

@@ -161,6 +161,21 @@ def getObject(ref):
if obj.TypeId in {"App::Part", "Assembly::AssemblyObject"} or isLinkGroup(obj):
continue
elif obj.isDerivedFrom("App::LocalCoordinateSystem"):
# 2 cases possible, either we have the LCS itself: "part.LCS."
# or we have a datum: "part.LCS.X_Axis"
if i + 1 < len(names):
obj2 = None
for obji in obj.OutList:
if obji.Name == names[i + 1]:
obj2 = obji
break
if obj2 and obj2.isDerivedFrom("App::DatumElement"):
return obj2
elif obj.isDerivedFrom("App::DatumElement"):
return obj
elif obj.TypeId == "PartDesign::Body":
if i + 1 < len(names):
obj2 = None
@@ -168,7 +183,7 @@ def getObject(ref):
if obji.Name == names[i + 1]:
obj2 = obji
break
if obj2 and isBodySubObject(obj2.TypeId):
if obj2 and isBodySubObject(obj2):
return obj2
return obj
@@ -185,7 +200,7 @@ def getObject(ref):
if obji.Name == names[i + 1]:
obj2 = obji
break
if obj2 and isBodySubObject(obj2.TypeId):
if obj2 and isBodySubObject(obj2):
return obj2
return obj
elif linked_obj.isDerivedFrom("Part::Feature"):
@@ -197,13 +212,12 @@ def getObject(ref):
return None
def isBodySubObject(typeId):
def isBodySubObject(obj):
return (
typeId == "Sketcher::SketchObject"
or typeId == "PartDesign::Point"
or typeId == "PartDesign::Line"
or typeId == "PartDesign::Plane"
or typeId == "PartDesign::CoordinateSystem"
obj.isDerivedFrom("Sketcher::SketchObject")
or obj.isDerivedFrom("PartDesign::Datum")
or obj.isDerivedFrom("App::DatumElement")
or obj.isDerivedFrom("App::LocalCoordinateSystem")
)
@@ -348,21 +362,6 @@ def getElementName(full_name):
if parts[-1] in {"X", "Y", "Z", "Point", "Line", "Plane"}:
return ""
# Case of origin objects
if parts[-1] == "":
if "X_Axis" in parts[-2]:
return "X_Axis"
if "Y_Axis" in parts[-2]:
return "Y_Axis"
if "Z_Axis" in parts[-2]:
return "Z_Axis"
if "XY_Plane" in parts[-2]:
return "XY_Plane"
if "XZ_Plane" in parts[-2]:
return "XZ_Plane"
if "YZ_Plane" in parts[-2]:
return "YZ_Plane"
return parts[-1]
@@ -946,16 +945,8 @@ def findPlacement(ref, ignoreVertex=False):
elt = getElementName(ref[1][0])
vtx = getElementName(ref[1][1])
# case of origin objects.
if elt == "X_Axis" or elt == "YZ_Plane":
return App.Placement(App.Vector(), App.Rotation(App.Vector(0, 1, 0), -90))
if elt == "Y_Axis" or elt == "XZ_Plane":
return App.Placement(App.Vector(), App.Rotation(App.Vector(1, 0, 0), 90))
if elt == "Z_Axis" or elt == "XY_Plane":
return App.Placement()
if not elt or not vtx:
# case of whole parts such as PartDesign::Body or PartDesign::CordinateSystem/Point/Line/Plane.
# case of whole parts such as PartDesign::Body or App/PartDesign::CordinateSystem/Point/Line/Plane.
return App.Placement()
plc = App.Placement()

View File

@@ -58,6 +58,7 @@
#include "ConicPy.h"
#include "CustomFeature.h"
#include "CylinderPy.h"
#include "Datums.h"
#include "DatumFeature.h"
#include "EllipsePy.h"
#include "FaceMaker.h"
@@ -534,6 +535,10 @@ PyMOD_INIT_FUNC(Part)
Part::GeomSurfaceOfRevolution ::init();
Part::GeomSurfaceOfExtrusion ::init();
Part::Datum ::init();
Part::DatumPlane ::init();
Part::DatumLine ::init();
Part::DatumPoint ::init();
Part::LocalCoordinateSystem ::init();
// Geometry2d types
Part::Geometry2d ::init();

View File

@@ -411,33 +411,18 @@ void AttachExtension::extensionOnChanged(const App::Property* prop)
bool bAttached = false;
try{
bAttached = positionBySupport();
} catch (Base::Exception &e) {
}
catch (Base::Exception &e) {
getExtendedObject()->setStatus(App::Error, true);
Base::Console().Error("PositionBySupport: %s\n",e.what());
//set error message - how?
} catch (Standard_Failure &e){
}
catch (Standard_Failure &e){
getExtendedObject()->setStatus(App::Error, true);
Base::Console().Error("PositionBySupport: %s\n",e.GetMessageString());
}
// Hide properties when not applicable to reduce user confusion
eMapMode mmode = eMapMode(this->MapMode.getValue());
bool modeIsPointOnCurve = mmode == mmNormalToPath ||
mmode == mmFrenetNB || mmode == mmFrenetTN || mmode == mmFrenetTB ||
mmode == mmRevolutionSection || mmode == mmConcentric;
// MapPathParameter is only used if there is a reference to one edge and not edge + vertex
bool hasOneRef = false;
if (_props.attacher && _props.attacher->subnames.size() == 1) {
hasOneRef = true;
}
this->MapPathParameter.setStatus(App::Property::Status::Hidden, !bAttached || !(modeIsPointOnCurve && hasOneRef));
this->MapReversed.setStatus(App::Property::Status::Hidden, !bAttached);
this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached);
getPlacement().setReadOnly(bAttached && mmode != mmTranslate); //for mmTranslate, orientation should remain editable even when attached.
updateSinglePropertyStatus(bAttached);
}
if (prop == &AttacherEngine) {
AttacherType.setValue(enumToClass(AttacherEngine.getValueAsString()));
@@ -508,27 +493,9 @@ void AttachExtension::onExtendedDocumentRestored()
restoreAttacherEngine(this);
// Hide properties when not applicable to reduce user confusion
bool bAttached = positionBySupport();
eMapMode mmode = eMapMode(this->MapMode.getValue());
bool modeIsPointOnCurve =
(mmode == mmNormalToPath ||
mmode == mmFrenetNB ||
mmode == mmFrenetTN ||
mmode == mmFrenetTB ||
mmode == mmRevolutionSection ||
mmode == mmConcentric);
// MapPathParameter is only used if there is a reference to one edge and not edge + vertex
bool hasOneRef = false;
if (_props.attacher && _props.attacher->subnames.size() == 1) {
hasOneRef = true;
}
this->MapPathParameter.setStatus(App::Property::Status::Hidden, !bAttached || !(modeIsPointOnCurve && hasOneRef));
this->MapReversed.setStatus(App::Property::Status::Hidden, !bAttached);
this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached);
getPlacement().setReadOnly(bAttached && mmode != mmTranslate); //for mmTranslate, orientation should remain editable even when attached.
updateSinglePropertyStatus(bAttached);
}
catch (Base::Exception&) {
}
@@ -536,7 +503,7 @@ void AttachExtension::onExtendedDocumentRestored()
}
}
void AttachExtension::updatePropertyStatus(bool bAttached, bool base)
void AttachExtension::updateSinglePropertyStatus(bool bAttached, bool base)
{
auto& props = base ? this->_baseProps : this->_props;
if (!props.mapMode) {
@@ -550,12 +517,9 @@ void AttachExtension::updatePropertyStatus(bool bAttached, bool base)
|| mmode == mmFrenetTB || mmode == mmRevolutionSection || mmode == mmConcentric);
// MapPathParameter is only used if there is a reference to one edge and not edge + vertex
bool hasOneRef = false;
if (props.attacher && props.attacher->subnames.size() == 1) {
hasOneRef = true;
}
props.mapPathParameter->setStatus(App::Property::Status::Hidden,
!bAttached || !(modeIsPointOnCurve && hasOneRef));
bool hasOneRef = props.attacher && props.attacher->subnames.size() == 1;
props.mapPathParameter->setStatus(App::Property::Status::Hidden, !bAttached || !(modeIsPointOnCurve && hasOneRef));
props.mapReversed->setStatus(App::Property::Status::Hidden, !bAttached);
if (base) {
@@ -564,11 +528,18 @@ void AttachExtension::updatePropertyStatus(bool bAttached, bool base)
else {
this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached);
if (getExtendedContainer()) {
getPlacement().setReadOnly(
bAttached && mmode != mmTranslate); // for mmTranslate, orientation should remain
// editable even when attached.
// for mmTranslate, orientation should remain editable even when attached.
getPlacement().setReadOnly(bAttached && mmode != mmTranslate);
}
updatePropertyStatus(bAttached, true);
}
}
void AttachExtension::updatePropertyStatus(bool bAttached, bool base)
{
updateSinglePropertyStatus(bAttached, base);
if (!base) {
updateSinglePropertyStatus(bAttached, true);
}
}

View File

@@ -35,7 +35,6 @@
#include <Base/Placement.h>
#include "Attacher.h"
#include "PartFeature.h"
namespace Part
@@ -146,7 +145,10 @@ protected:
public:
void updateAttacherVals(bool base = false) const;
// This update both _props and _baseProps if base = false
void updatePropertyStatus(bool attached, bool base = false);
// This update only _props if base = false
void updateSinglePropertyStatus(bool attached, bool base = false);
private:
struct _Properties: Properties

View File

@@ -27,6 +27,7 @@
# include <BRepAdaptor_Surface.hxx>
# include <BRepBuilderAPI_MakeEdge.hxx>
# include <BRepBuilderAPI_MakeFace.hxx>
# include <BRepBuilderAPI_MakeVertex.hxx>
# include <BRepExtrema_DistShapeShape.hxx>
# include <BRepGProp.hxx>
# include <BRepIntCurveSurface_Inter.hxx>
@@ -300,7 +301,8 @@ Base::Placement AttachEngine::placementFactory(const gp_Dir &ZAxis,
gp_Ax3 ax3;//OCC representation of the final placement
if (!makeYVertical) {
ax3 = gp_Ax3(Origin, ZAxis, XAxis);
} else if (!makeLegacyFlatFaceOrientation) {
}
else if (!makeLegacyFlatFaceOrientation) {
//align Y along Z, if possible
gp_Vec YAxis(0.0,0.0,1.0);
XAxis = YAxis.Crossed(gp_Vec(ZAxis));
@@ -309,7 +311,8 @@ Base::Placement AttachEngine::placementFactory(const gp_Dir &ZAxis,
XAxis = (gp_Vec(1,0,0)*ZAxis.Z()).Normalized();
}
ax3 = gp_Ax3(Origin, ZAxis, XAxis);
} else if (makeLegacyFlatFaceOrientation) {
}
else if (makeLegacyFlatFaceOrientation) {
//find out, to which axis of support Normal is closest to.
//The result will be written into pos variable (0..2 = X..Z)
if (!placeOfRef)
@@ -385,13 +388,11 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const
result.message = SuggestResult::srLinkBroken;
result.bestFitMode = mmDeactivated;
std::vector<App::GeoFeature*> parts;
std::vector<const TopoDS_Shape*> shapes;
std::vector<TopoDS_Shape> shapeStorage;
std::vector<eRefType> typeStr;
try{
readLinks(getRefObjects(),subnames, parts, shapes, shapeStorage, typeStr);
readLinks(getRefObjects(),subnames, shapes, shapeStorage, typeStr);
} catch (Base::Exception &err) {
result.references_Types = typeStr;
result.message = SuggestResult::srLinkBroken;
@@ -577,11 +578,10 @@ eRefType AttachEngine::getShapeType(const App::DocumentObject *obj, const std::s
//const_cast is worth here, to keep obj argument const. We are not going to write anything to obj through this temporary link.
tmpLink.setValue(const_cast<App::DocumentObject*>(obj), subshape.c_str());
std::vector<App::GeoFeature*> parts;
std::vector<const TopoDS_Shape*> shapes;
std::vector<TopoDS_Shape> copiedShapeStorage;
std::vector<eRefType> types;
readLinks(tmpLink.getValues(),tmpLink.getSubValues(), parts, shapes, copiedShapeStorage, types);
readLinks(tmpLink.getValues(), tmpLink.getSubValues(), shapes, copiedShapeStorage, types);
assert(types.size() == 1);
return types[0];
@@ -814,105 +814,79 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector<const TopoD
/*!
* \brief AttachEngine3D::readLinks
* \param parts
* \param shapes
* \param storage is a buffer storing what some of the pointers in shapes point to. It is needed, since
* subshapes are copied in the process (but copying a whole shape of an object can potentially be slow).
*/
void AttachEngine::readLinks(const std::vector<App::DocumentObject*> &objs,
const std::vector<std::string> &sub,
std::vector<App::GeoFeature*> &geofs,
void AttachEngine::readLinks(const std::vector<App::DocumentObject*>& objs,
const std::vector<std::string> &subs,
std::vector<const TopoDS_Shape*> &shapes,
std::vector<TopoDS_Shape> &storage,
std::vector<eRefType> &types)
{
geofs.resize(objs.size());
storage.reserve(objs.size());
shapes.resize(objs.size());
types.resize(objs.size());
for (std::size_t i = 0; i < objs.size(); i++) {
if (!objs[i]->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: attached to a non App::GeoFeature '"
<< objs[i]->getNameInDocument() << "'");
}
auto* geof = dynamic_cast<App::GeoFeature*>(objs[i]);
geofs[i] = geof;
Part::TopoShape shape;
if (geof->isDerivedFrom(App::Plane::getClassTypeId())) {
// obtain Z axis and origin of placement
Base::Vector3d norm;
geof->Placement.getValue().getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm);
Base::Vector3d org;
geof->Placement.getValue().multVec(Base::Vector3d(), org);
// make shape - an local-XY plane infinite face
gp_Pln plane = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z));
TopoDS_Shape myShape = BRepBuilderAPI_MakeFace(plane).Shape();
myShape.Infinite(true);
storage.emplace_back(myShape);
shapes[i] = &(storage[storage.size() - 1]);
if (!geof) {
// Accept App::Links to GeoFeatures
geof = dynamic_cast<App::GeoFeature*>(objs[i]->getLinkedObject());
if (!geof) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: attached to a non App::GeoFeature '" << objs[i]->getNameInDocument() << "'");
}
}
else if (geof->isDerivedFrom(App::Line::getClassTypeId())) {
// obtain X axis and origin of placement
// note an inconsistency: App::Line is along local X, PartDesign::DatumLine is along
// local Z.
Base::Vector3d dir;
geof->Placement.getValue().getRotation().multVec(Base::Vector3d(1.0, 0.0, 0.0), dir);
Base::Vector3d org;
geof->Placement.getValue().multVec(Base::Vector3d(), org);
// make shape - an infinite line along local X axis
gp_Lin line = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z));
TopoDS_Shape myShape = BRepBuilderAPI_MakeEdge(line).Shape();
myShape.Infinite(true);
storage.emplace_back(myShape);
shapes[i] = &(storage[storage.size() - 1]);
}
else {
try {
shape = Part::Feature::getTopoShape(geof, sub[i].c_str(), true);
for (;;) {
if (shape.isNull()) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: subshape not found "
<< objs[i]->getNameInDocument() << '.' << sub[i]);
}
if (shape.shapeType() != TopAbs_COMPOUND
|| shape.countSubShapes(TopAbs_SHAPE) != 1) {
break;
}
// auto extract the single sub-shape from a compound
shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
TopoDS_Shape myShape;
try {
// getTopoShape support fully qualified subnames and should return shape with correct
// global placement.
Part::TopoShape shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true);
for (;;) {
if (shape.isNull()) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: subshape not found "
<< objs[i]->getNameInDocument() << '.' << subs[i]);
}
storage.emplace_back(shape.getShape());
if (shape.shapeType() != TopAbs_COMPOUND
|| shape.countSubShapes(TopAbs_SHAPE) != 1) {
break;
}
// auto extract the single sub-shape from a compound
shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
}
catch (Standard_Failure& e) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
<< '.' << sub[i] << std::endl
<< e.GetMessageString());
}
catch (Base::CADKernelError& e) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
<< '.' << sub[i] << std::endl
<< e.what());
}
if (storage.back().IsNull()) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
<< sub[i]);
}
shapes[i] = &(storage.back());
myShape = shape.getShape();
}
catch (Standard_Failure& e) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
<< '.' << subs[i] << std::endl
<< e.GetMessageString());
}
catch (Base::CADKernelError& e) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
<< '.' << subs[i] << std::endl
<< e.what());
}
if (myShape.IsNull()) {
FC_THROWM(AttachEngineException,
"AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
<< subs[i]);
}
storage.emplace_back(myShape);
shapes[i] = &(storage.back());
// FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be
// considered later, when the need arises.
types[i] = getShapeType(*(shapes[i]));
if (sub[i].length() == 0) {
if (subs[i].length() == 0) {
types[i] = eRefType(types[i] | rtFlagHasPlacement);
}
}
}
void AttachEngine::throwWrongMode(eMapMode mmode)
@@ -977,6 +951,7 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement&
for (auto obj : objs) {
++i;
auto& sub = subnames[i];
obj = obj->getSubObject(sub.c_str());
auto& shadow = shadowSubs[i];
if (shadow.empty() || !Data::hasMissingElement(sub.c_str())) {
continue;
@@ -1165,20 +1140,20 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
throw ExceptionCancel(); // to be handled in positionBySupport, to not do anything if
// disabled
}
std::vector<App::GeoFeature*> parts;
std::vector<const TopoDS_Shape*> shapes;
std::vector<TopoDS_Shape> copiedShapeStorage;
std::vector<eRefType> types;
readLinks(objs, subs, parts, shapes, copiedShapeStorage, types);
readLinks(objs, subs, shapes, copiedShapeStorage, types);
if (parts.empty()) {
if (shapes.empty()) {
throw ExceptionCancel();
}
// common stuff for all map modes
gp_Pnt refOrg(0.0, 0.0, 0.0); // origin of linked object
Base::Placement Place = parts[0]->Placement.getValue();
refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
App::DocumentObject* subObj = objs[0]->getSubObject(subs[0].c_str());
Base::Placement Place = App::GeoFeature::getGlobalPlacement(subObj, objs[0], subs[0]);
Base::Vector3d vec = Place.getPosition();
gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object
// variables to derive the actual placement.
// They are to be set, depending on the mode:
@@ -2108,21 +2083,21 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
Base::Placement plm;
if (!bReUsed) {
std::vector<App::GeoFeature*> parts;
std::vector<const TopoDS_Shape*> shapes;
std::vector<TopoDS_Shape> copiedShapeStorage;
std::vector<eRefType> types;
readLinks(objs, subs, parts, shapes, copiedShapeStorage, types);
readLinks(objs, subs, shapes, copiedShapeStorage, types);
if (parts.empty()) {
if (shapes.empty()) {
throw ExceptionCancel();
}
// common stuff for all map modes
gp_Pnt refOrg(0.0, 0.0, 0.0);
Base::Placement Place = parts[0]->Placement.getValue();
refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
App::DocumentObject* subObj = objs[0]->getSubObject(subs[0].c_str());
Base::Placement Place = App::GeoFeature::getGlobalPlacement(subObj, objs[0], subs[0]);
Base::Vector3d vec = Place.getPosition();
gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object
// variables to derive the actual placement.
// They are to be set, depending on the mode:
@@ -2475,13 +2450,12 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector<App::DocumentOb
Base::Placement plm;
if (!bReUsed) {
std::vector<App::GeoFeature*> parts;
std::vector<const TopoDS_Shape*> shapes;
std::vector<TopoDS_Shape> copiedShapeStorage;
std::vector<eRefType> types;
readLinks(objs, subs, parts, shapes, copiedShapeStorage, types);
readLinks(objs, subs, shapes, copiedShapeStorage, types);
if (parts.empty()) {
if (shapes.empty()) {
throw ExceptionCancel();
}

View File

@@ -429,7 +429,7 @@ protected:
return ret;
}
static void readLinks(const std::vector<App::DocumentObject*> &objs,
const std::vector<std::string> &subs, std::vector<App::GeoFeature *> &geofs,
const std::vector<std::string> &subs,
std::vector<const TopoDS_Shape*>& shapes, std::vector<TopoDS_Shape> &storage,
std::vector<eRefType> &types);

View File

@@ -213,6 +213,8 @@ SET(Features_SRCS
CustomFeature.h
BodyBase.h
BodyBase.cpp
Datums.cpp
Datums.h
DatumFeature.cpp
DatumFeature.h
AttachExtension.h

View File

@@ -0,0 +1,63 @@
/***************************************************************************
* Copyright (c) 2024 Ondsel (PL Boyer) <development@ondsel.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#include "Datums.h"
using namespace Part;
using namespace Attacher;
PROPERTY_SOURCE_WITH_EXTENSIONS(Part::DatumPlane, App::Plane)
Part::DatumPlane::DatumPlane()
{
AttachExtension::initExtension(this);
this->setAttacher(new AttachEnginePlane);
}
PROPERTY_SOURCE_WITH_EXTENSIONS(Part::DatumLine, App::Line)
Part::DatumLine::DatumLine()
{
AttachExtension::initExtension(this);
this->setAttacher(new AttachEngineLine);
}
PROPERTY_SOURCE_WITH_EXTENSIONS(Part::DatumPoint, App::Point)
Part::DatumPoint::DatumPoint()
{
AttachExtension::initExtension(this);
this->setAttacher(new AttachEnginePoint);
}
PROPERTY_SOURCE_WITH_EXTENSIONS(Part::LocalCoordinateSystem, App::LocalCoordinateSystem)
Part::LocalCoordinateSystem::LocalCoordinateSystem()
{
AttachExtension::initExtension(this);
}

84
src/Mod/Part/App/Datums.h Normal file
View File

@@ -0,0 +1,84 @@
/***************************************************************************
* Copyright (c) 2024 Ondsel (PL Boyer) <development@ondsel.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef PART_DATUMS_H
#define PART_DATUMS_H
#include <App/Datums.h>
#include "AttachExtension.h"
namespace Part
{
class PartExport DatumPlane : public App::Plane, public AttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(Part::DatumPlane);
public:
DatumPlane();
~DatumPlane() override = default;
const char* getViewProviderName() const override {
return "PartGui::ViewProviderPlane";
}
};
class PartExport DatumLine : public App::Line, public AttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(Part::DatumLine);
public:
DatumLine();
~DatumLine() override = default;
const char* getViewProviderName() const override {
return "PartGui::ViewProviderLine";
}
};
class PartExport DatumPoint : public App::Point, public AttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(Part::DatumPoint);
public:
DatumPoint();
~DatumPoint() override = default;
const char* getViewProviderName() const override {
return "PartGui::ViewProviderPoint";
}
};
class PartExport LocalCoordinateSystem : public App::LocalCoordinateSystem, public AttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(Part::LocalCoordinateSystem);
public:
LocalCoordinateSystem();
~LocalCoordinateSystem() override = default;
const char* getViewProviderName() const override {
return "PartGui::ViewProviderLCS";
}
};
} //namespace Part
#endif // PART_DATUMS_H

View File

@@ -1018,7 +1018,7 @@ static TopoShape _getTopoShape(const App::DocumentObject* obj,
if (linked->isDerivedFrom(App::Line::getClassTypeId())) {
static TopoDS_Shape _shape;
if (_shape.IsNull()) {
BRepBuilderAPI_MakeEdge builder(gp_Lin(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)));
BRepBuilderAPI_MakeEdge builder(gp_Lin(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)));
_shape = builder.Shape();
_shape.Infinite(Standard_True);
}
@@ -1076,6 +1076,7 @@ static TopoShape _getTopoShape(const App::DocumentObject* obj,
shape = TopoShape(tag, hasher, _shape);
}
}
if (!shape.isNull()) {
shape.transformShape(mat * linkMat, false, true);
return shape;

View File

@@ -47,6 +47,7 @@
#include "ViewProvider.h"
#include "ViewProvider2DObject.h"
#include "ViewProviderAttachExtension.h"
#include "ViewProviderDatum.h"
#include "ViewProviderGridExtension.h"
#include "ViewProviderBoolean.h"
#include "ViewProviderBox.h"
@@ -167,6 +168,10 @@ PyMOD_INIT_FUNC(PartGui)
PartGui::ViewProviderGridExtensionPython ::init();
PartGui::ViewProviderSplineExtension ::init();
PartGui::ViewProviderSplineExtensionPython ::init();
PartGui::ViewProviderLine ::init();
PartGui::ViewProviderPlane ::init();
PartGui::ViewProviderPoint ::init();
PartGui::ViewProviderLCS ::init();
PartGui::ViewProviderPartExt ::init();
PartGui::ViewProviderPart ::init();
PartGui::ViewProviderPrimitive ::init();

View File

@@ -164,6 +164,8 @@ SET(PartGui_SRCS
ViewProvider.h
ViewProviderAttachExtension.h
ViewProviderAttachExtension.cpp
ViewProviderDatum.cpp
ViewProviderDatum.h
ViewProviderExt.cpp
ViewProviderExt.h
ViewProviderReference.cpp

View File

@@ -32,6 +32,7 @@
#endif
#include <App/Document.h>
#include <App/GeoFeature.h>
#include <App/DocumentObjectGroup.h>
#include <Base/Console.h>
#include <Base/Exception.h>
@@ -51,6 +52,8 @@
#include <Gui/View3DInventorViewer.h>
#include <Gui/WaitCursor.h>
#include <Mod/Part/App/Datums.h>
#include "BoxSelection.h"
#include "CrossSections.h"
#include "DlgBooleanOperation.h"
@@ -2211,6 +2214,193 @@ bool CmdPartSectionCut::isActive()
return hasActiveDocument();
}
//===========================================================================
// Part_CoordinateSystem
//===========================================================================
namespace {
QString getAutoGroupCommandStr()
// Helper function to get the python code to add the newly created object to the active Part/Body object if present
{
App::GeoFeature* activeObj = Gui::Application::Instance->activeView()->getActiveObject<App::GeoFeature*>(PDBODYKEY);
if (!activeObj) {
activeObj = Gui::Application::Instance->activeView()->getActiveObject<App::GeoFeature*>(PARTKEY);
}
if (activeObj) {
QString activeName = QString::fromLatin1(activeObj->getNameInDocument());
return QString::fromLatin1("App.ActiveDocument.getObject('%1\').addObject(obj)\n").arg(activeName);
}
return QString::fromLatin1("# Object created at document root.");
}
}
DEF_STD_CMD_A(CmdPartCoordinateSystem)
CmdPartCoordinateSystem::CmdPartCoordinateSystem()
: Command("Part_CoordinateSystem")
{
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Create a coordinate system");
sToolTipText = QT_TR_NOOP("A coordinate system object that can be attached to other objects.");
sWhatsThis = "Part_CoordinateSystem";
sStatusTip = sToolTipText;
sPixmap = "Std_CoordinateSystem";
}
void CmdPartCoordinateSystem::activated(int iMsg)
{
Q_UNUSED(iMsg);
openCommand(QT_TRANSLATE_NOOP("Command", "Add a coordinate system"));
std::string name = getUniqueObjectName("LCS");
doCommand(Doc, "obj = App.activeDocument().addObject('Part::LocalCoordinateSystem','%s')", name.c_str());
doCommand(Doc, getAutoGroupCommandStr().toUtf8());
doCommand(Doc, "obj.Visibility = True");
doCommand(Doc, "obj.ViewObject.doubleClicked()");
}
bool CmdPartCoordinateSystem::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Plane
//===========================================================================
DEF_STD_CMD_A(CmdPartPlane)
CmdPartPlane::CmdPartPlane()
: Command("Part_Plane")
{
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Create a datum plane");
sToolTipText = QT_TR_NOOP("A plane object that can be attached to other objects.");
sWhatsThis = "Part_Plane";
sStatusTip = sToolTipText;
sPixmap = "Std_Plane";
}
void CmdPartPlane::activated(int iMsg)
{
Q_UNUSED(iMsg);
openCommand(QT_TRANSLATE_NOOP("Command", "Add a datum plane"));
std::string name = getUniqueObjectName("Plane");
doCommand(Doc, "obj = App.activeDocument().addObject('Part::DatumPlane','%s')", name.c_str());
doCommand(Doc, getAutoGroupCommandStr().toUtf8());
doCommand(Doc, "obj.ViewObject.doubleClicked()");
}
bool CmdPartPlane::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Line
//===========================================================================
DEF_STD_CMD_A(CmdPartLine)
CmdPartLine::CmdPartLine()
: Command("Part_Line")
{
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Create a datum line");
sToolTipText = QT_TR_NOOP("A line object that can be attached to other objects.");
sWhatsThis = "Part_Line";
sStatusTip = sToolTipText;
sPixmap = "Std_Axis";
}
void CmdPartLine::activated(int iMsg)
{
Q_UNUSED(iMsg);
openCommand(QT_TRANSLATE_NOOP("Command", "Add a datum line"));
std::string name = getUniqueObjectName("Line");
doCommand(Doc, "obj = App.activeDocument().addObject('Part::DatumLine','%s')", name.c_str());
doCommand(Doc, getAutoGroupCommandStr().toUtf8());
doCommand(Doc, "obj.ViewObject.doubleClicked()");
}
bool CmdPartLine::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Point
//===========================================================================
DEF_STD_CMD_A(CmdPartPoint)
CmdPartPoint::CmdPartPoint()
: Command("Part_Point")
{
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Create a datum point");
sToolTipText = QT_TR_NOOP("A point object that can be attached to other objects.");
sWhatsThis = "Part_Point";
sStatusTip = sToolTipText;
sPixmap = "Std_Point";
}
void CmdPartPoint::activated(int iMsg)
{
Q_UNUSED(iMsg);
openCommand(QT_TRANSLATE_NOOP("Command", "Add a datum point"));
std::string name = getUniqueObjectName("Point");
doCommand(Doc, "obj = App.activeDocument().addObject('Part::DatumPoint','%s')", name.c_str());
doCommand(Doc, getAutoGroupCommandStr().toUtf8());
doCommand(Doc, "obj.ViewObject.doubleClicked()");
}
bool CmdPartPoint::isActive()
{
return hasActiveDocument();
}
//===========================================================================
// Part_Datums
//===========================================================================
class CmdPartDatums : public Gui::GroupCommand
{
public:
CmdPartDatums()
: GroupCommand("Part_Datums")
{
sGroup = QT_TR_NOOP("Part");
sMenuText = QT_TR_NOOP("Create a datum");
sToolTipText = QT_TR_NOOP("Create a datum object (LCS, Plane, Line, Point) that can be attached to other objects.");
sWhatsThis = "Part_Datums";
sStatusTip = sToolTipText;
setCheckable(false);
addCommand("Part_CoordinateSystem");
addCommand("Part_Plane");
addCommand("Part_Line");
addCommand("Part_Point");
}
const char* className() const override
{
return "CmdPartDatums";
}
bool isActive() override
{
return hasActiveDocument();
}
};
//---------------------------------------------------------------
void CreatePartCommands()
@@ -2256,4 +2446,10 @@ void CreatePartCommands()
rcCmdMgr.addCommand(new CmdBoxSelection());
rcCmdMgr.addCommand(new CmdPartProjectionOnSurface());
rcCmdMgr.addCommand(new CmdPartSectionCut());
rcCmdMgr.addCommand(new CmdPartCoordinateSystem());
rcCmdMgr.addCommand(new CmdPartPlane());
rcCmdMgr.addCommand(new CmdPartLine());
rcCmdMgr.addCommand(new CmdPartPoint());
rcCmdMgr.addCommand(new CmdPartDatums());
}

View File

@@ -33,6 +33,7 @@
#include <App/Application.h>
#include <App/Document.h>
#include <App/ElementNamingUtils.h>
#include <App/ObjectIdentifier.h>
#include <App/Datums.h>
#include <App/Part.h>
@@ -43,6 +44,7 @@
#include <Gui/DocumentObserver.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Base/Tools.h>
#include <Mod/Part/App/AttachExtension.h>
#include <Mod/Part/App/DatumFeature.h>
#include <Mod/Part/Gui/AttacherTexts.h>
@@ -62,29 +64,22 @@ namespace sp = std::placeholders;
// Create reference name from PropertyLinkSub values in a translatable fashion
const QString makeRefString(const App::DocumentObject* obj, const std::string& sub)
{
if (!obj)
if (!obj) {
return QObject::tr("No reference selected");
if (obj->isDerivedFrom<App::DatumElement>() ||
obj->isDerivedFrom<Part::Datum>())
// App::Plane, Line or Datum feature
return QString::fromLatin1(obj->getNameInDocument());
if ((sub.size() > 4) && (sub.substr(0,4) == "Face")) {
int subId = std::atoi(&sub[4]);
return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Face") + QString::number(subId);
} else if ((sub.size() > 4) && (sub.substr(0,4) == "Edge")) {
int subId = std::atoi(&sub[4]);
return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Edge") + QString::number(subId);
} else if ((sub.size() > 6) && (sub.substr(0,6) == "Vertex")) {
int subId = std::atoi(&sub[6]);
return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Vertex") + QString::number(subId);
} else {
//something else that face/edge/vertex. Can be empty string.
return QString::fromLatin1(obj->getNameInDocument())
+ (sub.length()>0 ? QString::fromLatin1(":") : QString())
+ QString::fromLatin1(sub.c_str());
}
if (obj->isDerivedFrom<App::DatumElement>() || obj->isDerivedFrom<Part::Datum>()) {
return QString::fromLatin1(obj->getNameInDocument());
}
// Hide the TNP string from the user. ie show "Body.Pad.Face6" and not :
// "Body.Pad.;#a:1;:G0;XTR;:Hc94:8,F.Face6"
App::ElementNamePair el;
App::GeoFeature::resolveElement(obj, sub.c_str(), el, true);
return QString::fromLatin1(obj->getNameInDocument())
+ (sub.length() > 0 ? QString::fromLatin1(":") : QString())
+ QString::fromLatin1(el.oldName.c_str());
}
void TaskAttacher::makeRefStrings(std::vector<QString>& refstrings, std::vector<std::string>& refnames) {
@@ -98,17 +93,18 @@ void TaskAttacher::makeRefStrings(std::vector<QString>& refstrings, std::vector<
// for Origin or Datum features refnames is empty but we need a non-empty return value
if (refnames[r].empty())
refnames[r] = refs[r]->getNameInDocument();
} else {
}
else {
refstrings.push_back(QObject::tr("No reference selected"));
refnames.emplace_back("");
}
}
}
TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidget *parent,
QString picture, QString text, TaskAttacher::VisibilityFunction visFunc)
TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject* ViewProvider, QWidget* parent,
QString picture, QString text, TaskAttacher::VisibilityFunction visFunc)
: TaskBox(Gui::BitmapFactory().pixmap(picture.toLatin1()), text, true, parent)
, SelectionObserver(ViewProvider)
, SelectionObserver(ViewProvider, true, Gui::ResolveMode::NoResolve)
, ViewProvider(ViewProvider)
, ui(new Ui_TaskAttacher)
, visibilityFunc(visFunc)
@@ -125,37 +121,37 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidge
// clang-format off
connect(ui->attachmentOffsetX, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskAttacher::onAttachmentOffsetXChanged);
this, &TaskAttacher::onAttachmentOffsetXChanged);
connect(ui->attachmentOffsetY, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskAttacher::onAttachmentOffsetYChanged);
this, &TaskAttacher::onAttachmentOffsetYChanged);
connect(ui->attachmentOffsetZ, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskAttacher::onAttachmentOffsetZChanged);
this, &TaskAttacher::onAttachmentOffsetZChanged);
connect(ui->attachmentOffsetYaw, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskAttacher::onAttachmentOffsetYawChanged);
this, &TaskAttacher::onAttachmentOffsetYawChanged);
connect(ui->attachmentOffsetPitch, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskAttacher::onAttachmentOffsetPitchChanged);
this, &TaskAttacher::onAttachmentOffsetPitchChanged);
connect(ui->attachmentOffsetRoll, qOverload<double>(&Gui::QuantitySpinBox::valueChanged),
this, &TaskAttacher::onAttachmentOffsetRollChanged);
this, &TaskAttacher::onAttachmentOffsetRollChanged);
connect(ui->checkBoxFlip, &QCheckBox::toggled,
this, &TaskAttacher::onCheckFlip);
this, &TaskAttacher::onCheckFlip);
connect(ui->buttonRef1, &QPushButton::clicked,
this, &TaskAttacher::onButtonRef1);
this, &TaskAttacher::onButtonRef1);
connect(ui->lineRef1, &QLineEdit::textEdited,
this, &TaskAttacher::onRefName1);
this, &TaskAttacher::onRefName1);
connect(ui->buttonRef2, &QPushButton::clicked,
this, &TaskAttacher::onButtonRef2);
this, &TaskAttacher::onButtonRef2);
connect(ui->lineRef2, &QLineEdit::textEdited,
this, &TaskAttacher::onRefName2);
this, &TaskAttacher::onRefName2);
connect(ui->buttonRef3, &QPushButton::clicked,
this, &TaskAttacher::onButtonRef3);
this, &TaskAttacher::onButtonRef3);
connect(ui->lineRef3, &QLineEdit::textEdited,
this, &TaskAttacher::onRefName3);
this, &TaskAttacher::onRefName3);
connect(ui->buttonRef4, &QPushButton::clicked,
this, &TaskAttacher::onButtonRef4);
this, &TaskAttacher::onButtonRef4);
connect(ui->lineRef4, &QLineEdit::textEdited,
this, &TaskAttacher::onRefName4);
this, &TaskAttacher::onRefName4);
connect(ui->listOfModes, &QListWidget::itemSelectionChanged,
this, &TaskAttacher::onModeSelect);
this, &TaskAttacher::onModeSelect);
// clang-format on
this->groupLayout()->addWidget(proxy);
@@ -205,19 +201,20 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidge
this->iActiveRef = 0;
else
this->iActiveRef = -1;
if (pcAttach->AttachmentSupport.getSize() == 0){
if (pcAttach->AttachmentSupport.getSize() == 0) {
autoNext = true;
} else {
}
else {
autoNext = false;
}
ui->attachmentOffsetX->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Base.x")));
ui->attachmentOffsetY->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Base.y")));
ui->attachmentOffsetZ->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Base.z")));
ui->attachmentOffsetX->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Base.x")));
ui->attachmentOffsetY->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Base.y")));
ui->attachmentOffsetZ->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Base.z")));
ui->attachmentOffsetYaw->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Yaw")));
ui->attachmentOffsetPitch->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Pitch")));
ui->attachmentOffsetRoll->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Roll")));
ui->attachmentOffsetYaw->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Yaw")));
ui->attachmentOffsetPitch->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Pitch")));
ui->attachmentOffsetRoll->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Roll")));
visibilityAutomation(true);
updateAttachmentOffsetUI();
@@ -234,6 +231,8 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidge
Gui::Document* document = Gui::Application::Instance->getDocument(ViewProvider->getObject()->getDocument());
connectDelObject = document->signalDeletedObject.connect(bnd1);
connectDelDocument = document->signalDeleteDocument.connect(bnd2);
handleInitialSelection();
}
TaskAttacher::~TaskAttacher()
@@ -293,10 +292,11 @@ void TaskAttacher::updateReferencesUI()
pcAttach->attacher().suggestMapModes(this->lastSuggestResult);
if (this->lastSuggestResult.message != SuggestResult::srOK) {
if(!this->lastSuggestResult.nextRefTypeHint.empty()){
if (!this->lastSuggestResult.nextRefTypeHint.empty()) {
//message = "Need more references";
}
} else {
}
else {
completed = true;
}
@@ -314,24 +314,29 @@ bool TaskAttacher::updatePreview()
Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType<Part::AttachExtension>();
QString errMessage;
bool attached = false;
try{
try {
attached = pcAttach->positionBySupport();
} catch (Base::Exception &err){
}
catch (Base::Exception& err) {
errMessage = QCoreApplication::translate("Exception", err.what());
} catch (Standard_Failure &err){
}
catch (Standard_Failure& err) {
errMessage = tr("OCC error: %1").arg(QString::fromLatin1(err.GetMessageString()));
} catch (...) {
}
catch (...) {
errMessage = tr("unknown error");
}
if (errMessage.length()>0){
if (errMessage.length() > 0) {
ui->message->setText(tr("Attachment mode failed: %1").arg(errMessage));
ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}"));
} else {
if (!attached){
}
else {
if (!attached) {
ui->message->setText(tr("Not attached"));
ui->message->setStyleSheet(QString());
} else {
std::vector<QString> strs = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(),eMapMode(pcAttach->MapMode.getValue()));
}
else {
std::vector<QString> strs = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(), eMapMode(pcAttach->MapMode.getValue()));
ui->message->setText(tr("Attached with mode %1").arg(strs[0]));
ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: green;}"));
}
@@ -345,47 +350,193 @@ bool TaskAttacher::updatePreview()
QLineEdit* TaskAttacher::getLine(unsigned idx)
{
switch(idx) {
case 0: return ui->lineRef1;
case 1: return ui->lineRef2;
case 2: return ui->lineRef3;
case 3: return ui->lineRef4;
default: return nullptr;
switch (idx) {
case 0: return ui->lineRef1;
case 1: return ui->lineRef2;
case 2: return ui->lineRef3;
case 3: return ui->lineRef4;
default: return nullptr;
}
}
void TaskAttacher::findCorrectObjAndSubInThisContext(App::DocumentObject*& rootObj, std::string& sub)
{
// The reference that we store must take into account the hierarchy of geoFeatures. For example:
// - Part
// - - Cube
// - Sketch
// if sketch is attached to Cube.Face1 then it must store Part:Cube.Face3 as Sketch is outside of Part.
// - Part
// - - Cube
// - - Sketch
// In this example it must store Cube:Face3 because Sketch is inside Part, sibling of Cube.
// So placement of Part is already taken into account.
// - Part1
// - - Part2
// - - - Cube
// - - Sketch
// In this example it must store Part2:Cube.Face3 since Part1 is already taken into account.
// - Part1
// - - Part2
// - - - Cube
// - - Part3
// - - - Sketch
// In this example it's not possible because Sketch has Part3 placement. So it should be rejected
// So we need to take the selection object and subname, and process them to get the correct obj/sub based
// on attached and attaching objects positions.
std::vector<std::string> names = Base::Tools::splitSubName(sub);
if (!rootObj || names.size() < 2) {
return;
}
names.insert(names.begin(), rootObj->getNameInDocument());
App::Document* doc = rootObj->getDocument();
App::DocumentObject* attachingObj = ViewProvider->getObject(); // Attaching object
App::DocumentObject* subObj = rootObj->getSubObject(sub.c_str()); // Object being attached.
if (!subObj || subObj == rootObj) {
// Case of root object. We don't need to modify it.
return;
}
if (subObj == attachingObj) {
//prevent self-referencing
rootObj = nullptr;
return;
}
// Check if attachingObj is a root object. if so we keep the full path.
auto* group = App::GeoFeatureGroupExtension::getGroupOfObject(attachingObj);
if (!group) {
if (attachingObj->getDocument() != rootObj->getDocument()) {
// If it's not in same document then it's not a good selection
rootObj = nullptr;
}
// if it's same document we keep the rootObj and sub unchanged.
return;
}
bool groupPassed = false;
for (size_t i = 0; i < names.size(); ++i) {
App::DocumentObject* obj = doc->getObject(names[i].c_str());
if (!obj) {
Base::Console().TranslatedUserError("TaskAttacher",
"Unsuitable selection: '%s' cannot be attached to '%s' from within it's group '%s'.\n",
attachingObj->getFullLabel(), subObj->getFullLabel(), group->getFullLabel());
rootObj = nullptr;
return;
}
if (groupPassed) {
rootObj = obj;
// Rebuild 'sub' starting from the next element after the current 'name'
sub = "";
for (size_t j = i + 1; j < names.size(); ++j) {
sub += names[j];
if (j != names.size() - 1) {
sub += "."; // Add a period between elements
}
}
return;
}
// In case the attaching object is in a link to a part.
// For instance :
// - Part1
// - - LinkToPart2
// - - - Cube
// - - - Sketch
obj = obj->getLinkedObject();
if (obj == group) {
groupPassed = true;
}
}
// if we reach this point it means that attaching object's group is outside of
// the scope of the attached object. For instance:
// - Part1
// - - Part2
// - - - Cube
// - - Part3
// - - - Sketch
// In this case the selection is not acceptable.
rootObj = nullptr;
}
void TaskAttacher::handleInitialSelection()
{
// We handle initial selection only if it is not attached yet.
App::DocumentObject* obj = ViewProvider->getObject();
Part::AttachExtension* pcAttach = obj->getExtensionByType<Part::AttachExtension>();
std::vector<App::DocumentObject*> refs = pcAttach->AttachmentSupport.getValues();
if (!refs.empty()) {
return;
}
std::vector<SubAndObjName> subAndObjNamePairs;
auto sel = Gui::Selection().getSelectionEx("",
App::DocumentObject::getClassTypeId(), Gui::ResolveMode::NoResolve);
for (auto& selObj : sel) {
std::vector<std::string> subs = selObj.getSubNames();
std::string objName = selObj.getFeatName();
for (auto& sub : subs) {
SubAndObjName objSubName = { objName, sub };
subAndObjNamePairs.push_back(objSubName);
}
}
addToReference(subAndObjNamePairs);
}
void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (!ViewProvider)
if (!ViewProvider) {
return;
}
if (msg.Type == Gui::SelectionChanges::AddSelection) {
if (iActiveRef < 0)
return;
SubAndObjName pair = { msg.pObjectName, msg.pSubName };
addToReference(pair);
}
}
// Note: The validity checking has already been done in ReferenceSelection.cpp
Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType<Part::AttachExtension>();
void TaskAttacher::addToReference(const std::vector<SubAndObjName>& pairs)
{
if (iActiveRef < 0) {
return;
}
// Note: The validity checking has already been done in ReferenceSelection.cpp
App::DocumentObject* obj = ViewProvider->getObject();
Part::AttachExtension* pcAttach = obj->getExtensionByType<Part::AttachExtension>();
for (auto& pair : pairs) {
std::vector<App::DocumentObject*> refs = pcAttach->AttachmentSupport.getValues();
std::vector<std::string> refnames = pcAttach->AttachmentSupport.getSubValues();
App::DocumentObject* selObj = ViewProvider->getObject()->getDocument()->getObject(msg.pObjectName);
if (!selObj || selObj == ViewProvider->getObject())//prevent self-referencing
return;
std::string subname = msg.pSubName;
App::DocumentObject* selObj = obj->getDocument()->getObject(pair.objName.c_str());
std::string subname = pair.subName;
findCorrectObjAndSubInThisContext(selObj, subname);
if (!selObj) {
return;
}
// Remove subname for planes and datum features
if (selObj->isDerivedFrom<App::DatumElement>() ||
selObj->isDerivedFrom<Part::Datum>())
if (selObj->isDerivedFrom<App::DatumElement>() || selObj->isDerivedFrom<Part::Datum>()) {
subname = "";
}
// eliminate duplicate selections
for (size_t r = 0; r < refs.size(); r++)
if ((refs[r] == selObj) && (refnames[r] == subname))
for (size_t r = 0; r < refs.size(); r++) {
if ((refs[r] == selObj) && (refnames[r] == subname)) {
return;
}
}
if (autoNext && iActiveRef > 0 && iActiveRef == static_cast<int>(refnames.size())){
if (refs[iActiveRef-1] == selObj
&& refnames[iActiveRef-1].length() != 0 && subname.length() == 0){
if (autoNext && iActiveRef > 0 && iActiveRef == static_cast<int>(refnames.size())) {
if (refs[iActiveRef - 1] == selObj
&& refnames[iActiveRef - 1].length() != 0 && subname.length() == 0) {
//A whole object was selected by clicking it twice. Fill it
//into previous reference, where a sub-named reference filled by
//the first click is already stored.
@@ -396,31 +547,13 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg)
if (iActiveRef < static_cast<int>(refs.size())) {
refs[iActiveRef] = selObj;
refnames[iActiveRef] = subname;
} else {
}
else {
refs.push_back(selObj);
refnames.push_back(subname);
}
//bool error = false;
try {
pcAttach->AttachmentSupport.setValues(refs, refnames);
updateListOfModes();
eMapMode mmode = getActiveMapMode();//will be mmDeactivated, if selected or if no modes are available
if(mmode == mmDeactivated){
//error = true;
this->completed = false;
} else {
this->completed = true;
}
pcAttach->MapMode.setValue(mmode);
selectMapMode(mmode);
updatePreview();
}
catch(Base::Exception& e) {
//error = true;
ui->message->setText(QCoreApplication::translate("Exception", e.what()));
ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}"));
}
pcAttach->AttachmentSupport.setValues(refs, refnames);
QLineEdit* line = getLine(iActiveRef);
if (line) {
@@ -431,17 +564,43 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg)
}
if (autoNext) {
if (iActiveRef == -1){
if (iActiveRef == -1) {
//nothing to do
} else if (iActiveRef == 4 || this->lastSuggestResult.nextRefTypeHint.empty()){
}
else if (iActiveRef == 4 || this->lastSuggestResult.nextRefTypeHint.empty()) {
iActiveRef = -1;
} else {
}
else {
iActiveRef++;
}
}
updateReferencesUI();
}
try {
updateListOfModes();
eMapMode mmode = getActiveMapMode(); //will be mmDeactivated, if selected or if no modes are available
if (mmode == mmDeactivated) {
//error = true;
this->completed = false;
}
else {
this->completed = true;
}
pcAttach->MapMode.setValue(mmode);
selectMapMode(mmode);
updatePreview();
}
catch (Base::Exception& e) {
ui->message->setText(QCoreApplication::translate("Exception", e.what()));
ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}"));
}
updateReferencesUI();
}
void TaskAttacher::addToReference(SubAndObjName pair)
{
addToReference({ pair });
}
void TaskAttacher::onAttachmentOffsetChanged(double /*val*/, int idx)
@@ -462,17 +621,17 @@ void TaskAttacher::onAttachmentOffsetChanged(double /*val*/, int idx)
if (idx == 2) {
pos.z = ui->attachmentOffsetZ->value().getValueAs(Base::Quantity::MilliMetre);
}
if (idx >= 0 && idx <= 2){
if (idx >= 0 && idx <= 2) {
pl.setPosition(pos);
}
if (idx >= 3 && idx <= 5){
if (idx >= 3 && idx <= 5) {
double yaw, pitch, roll;
yaw = ui->attachmentOffsetYaw->value().getValueAs(Base::Quantity::Degree);
pitch = ui->attachmentOffsetPitch->value().getValueAs(Base::Quantity::Degree);
roll = ui->attachmentOffsetRoll->value().getValueAs(Base::Quantity::Degree);
Base::Rotation rot;
rot.setYawPitchRoll(yaw,pitch,roll);
rot.setYawPitchRoll(yaw, pitch, roll);
pl.setRotation(rot);
}
@@ -526,7 +685,8 @@ void TaskAttacher::onButtonRef(const bool checked, unsigned idx)
if (checked) {
Gui::Selection().clearSelection();
iActiveRef = idx;
} else {
}
else {
iActiveRef = -1;
}
updateRefButton(0);
@@ -619,12 +779,15 @@ void TaskAttacher::onRefName(const QString& text, unsigned idx)
if (obj->isDerivedFrom<App::Plane>()) {
// everything is OK (we assume a Part can only have exactly 3 App::Plane objects located at the base of the feature tree)
subElement.clear();
} else if (obj->isDerivedFrom<App::Line>()) {
}
else if (obj->isDerivedFrom<App::Line>()) {
// everything is OK (we assume a Part can only have exactly 3 App::Line objects located at the base of the feature tree)
subElement.clear();
} else if (obj->isDerivedFrom<Part::Datum>()) {
}
else if (obj->isDerivedFrom<Part::Datum>()) {
subElement.clear();
} else {
}
else {
// TODO: check validity of the text that was entered: Does subElement actually reference to an element on the obj?
auto getSubshapeName = [](const QString& part) -> std::string {
@@ -672,7 +835,8 @@ void TaskAttacher::onRefName(const QString& text, unsigned idx)
if (idx < refs.size()) {
refs[idx] = obj;
refnames[idx] = subElement;
} else {
}
else {
refs.push_back(obj);
refnames.emplace_back(subElement);
}
@@ -690,12 +854,12 @@ void TaskAttacher::updateRefButton(int idx)
return;
QAbstractButton* b;
switch(idx){
case 0: b = ui->buttonRef1; break;
case 1: b = ui->buttonRef2; break;
case 2: b = ui->buttonRef3; break;
case 3: b = ui->buttonRef4; break;
default: throw Base::IndexError("button index out of range");
switch (idx) {
case 0: b = ui->buttonRef1; break;
case 1: b = ui->buttonRef2; break;
case 2: b = ui->buttonRef3; break;
case 3: b = ui->buttonRef4; break;
default: throw Base::IndexError("button index out of range");
}
Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType<Part::AttachExtension>();
@@ -713,10 +877,12 @@ void TaskAttacher::updateRefButton(int idx)
if (iActiveRef == idx) {
b->setText(tr("Selecting..."));
} else if (idx < static_cast<int>(this->lastSuggestResult.references_Types.size())){
}
else if (idx < static_cast<int>(this->lastSuggestResult.references_Types.size())) {
b->setText(AttacherGui::getShapeTypeText(this->lastSuggestResult.references_Types[idx]));
} else {
b->setText(tr("Reference%1").arg(idx+1));
}
else {
b->setText(tr("Reference%1").arg(idx + 1));
}
}
@@ -740,9 +906,9 @@ void TaskAttacher::updateAttachmentOffsetUI()
ui->attachmentOffsetPitch->blockSignals(bBlock);
ui->attachmentOffsetRoll->blockSignals(bBlock);
ui->attachmentOffsetX->setValue(Base::Quantity(pos.x,Base::Unit::Length));
ui->attachmentOffsetY->setValue(Base::Quantity(pos.y,Base::Unit::Length));
ui->attachmentOffsetZ->setValue(Base::Quantity(pos.z,Base::Unit::Length));
ui->attachmentOffsetX->setValue(Base::Quantity(pos.x, Base::Unit::Length));
ui->attachmentOffsetY->setValue(Base::Quantity(pos.y, Base::Unit::Length));
ui->attachmentOffsetZ->setValue(Base::Quantity(pos.z, Base::Unit::Length));
ui->attachmentOffsetYaw->setValue(yaw);
ui->attachmentOffsetPitch->setValue(pitch);
ui->attachmentOffsetRoll->setValue(roll);
@@ -750,13 +916,13 @@ void TaskAttacher::updateAttachmentOffsetUI()
auto expressions = ViewProvider->getObject()->ExpressionEngine.getExpressions();
bool bRotationBound = false;
bRotationBound = bRotationBound ||
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Angle"))) != expressions.end();
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Angle"))) != expressions.end();
bRotationBound = bRotationBound ||
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Axis.x"))) != expressions.end();
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Axis.x"))) != expressions.end();
bRotationBound = bRotationBound ||
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Axis.y"))) != expressions.end();
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Axis.y"))) != expressions.end();
bRotationBound = bRotationBound ||
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Axis.z"))) != expressions.end();
expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Axis.z"))) != expressions.end();
ui->attachmentOffsetYaw->setEnabled(!bRotationBound);
ui->attachmentOffsetPitch->setEnabled(!bRotationBound);
@@ -794,22 +960,23 @@ void TaskAttacher::updateListOfModes()
this->lastSuggestResult.bestFitMode = mmDeactivated;
size_t lastValidModeItemIndex = mmDummy_NumberOfModes;
if (pcAttach->AttachmentSupport.getSize() > 0){
if (pcAttach->AttachmentSupport.getSize() > 0) {
pcAttach->attacher().suggestMapModes(this->lastSuggestResult);
modesInList = this->lastSuggestResult.allApplicableModes;
modesInList.insert(modesInList.begin(), mmDeactivated); // always have the option to choose Deactivated mode
//add reachable modes to the list, too, but gray them out (using lastValidModeItemIndex, later)
lastValidModeItemIndex = modesInList.size()-1;
for(std::pair<const eMapMode, refTypeStringList> &rm: this->lastSuggestResult.reachableModes){
lastValidModeItemIndex = modesInList.size() - 1;
for (std::pair<const eMapMode, refTypeStringList>& rm : this->lastSuggestResult.reachableModes) {
modesInList.push_back(rm.first);
}
} else {
}
else {
//no references - display all modes
modesInList.clear();
modesInList.push_back(mmDeactivated);
for( int mmode = 0 ; mmode < mmDummy_NumberOfModes ; mmode++){
for (int mmode = 0; mmode < mmDummy_NumberOfModes; mmode++) {
if (pcAttach->attacher().modeEnabled[mmode])
modesInList.push_back(eMapMode(mmode));
}
@@ -820,40 +987,42 @@ void TaskAttacher::updateListOfModes()
ui->listOfModes->clear();
QListWidgetItem* iSelect = nullptr;
if (!modesInList.empty()) {
for (size_t i = 0 ; i < modesInList.size() ; ++i){
for (size_t i = 0; i < modesInList.size(); ++i) {
eMapMode mmode = modesInList[i];
std::vector<QString> mstr = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(),mmode);
std::vector<QString> mstr = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(), mmode);
ui->listOfModes->addItem(mstr[0]);
QListWidgetItem* item = ui->listOfModes->item(i);
QString tooltip = mstr[1];
if (mmode != mmDeactivated) {
tooltip += QString::fromLatin1("\n\n%1\n%2")
.arg(tr("Reference combinations:"),
AttacherGui::getRefListForMode(pcAttach->attacher(),mmode).join(QString::fromLatin1("\n")));
.arg(tr("Reference combinations:"),
AttacherGui::getRefListForMode(pcAttach->attacher(), mmode).join(QString::fromLatin1("\n")));
}
item->setToolTip(tooltip);
if (mmode == curMode && curMode != mmDeactivated)
iSelect = ui->listOfModes->item(i);
if (i > lastValidModeItemIndex){
if (i > lastValidModeItemIndex) {
//potential mode - can be reached by selecting more stuff
item->setFlags(item->flags() & ~(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable));
refTypeStringList &extraRefs = this->lastSuggestResult.reachableModes[mmode];
if (extraRefs.size() == 1){
refTypeStringList& extraRefs = this->lastSuggestResult.reachableModes[mmode];
if (extraRefs.size() == 1) {
QStringList buf;
for(eRefType rt : extraRefs[0]){
for (eRefType rt : extraRefs[0]) {
buf.append(AttacherGui::getShapeTypeText(rt));
}
item->setText(tr("%1 (add %2)").arg(
item->text(),
buf.join(QString::fromLatin1("+"))
));
} else {
item->text(),
buf.join(QString::fromLatin1("+"))
));
}
else {
item->setText(tr("%1 (add more references)").arg(item->text()));
}
} else if (mmode == this->lastSuggestResult.bestFitMode){
}
else if (mmode == this->lastSuggestResult.bestFitMode) {
//suggested mode - make bold
QFont fnt = item->font();
fnt.setBold(true);
@@ -873,7 +1042,7 @@ void TaskAttacher::updateListOfModes()
void TaskAttacher::selectMapMode(eMapMode mmode) {
ui->listOfModes->blockSignals(true);
for (size_t i = 0; i < modesInList.size(); ++i) {
for (size_t i = 0; i < modesInList.size(); ++i) {
if (modesInList[i] == mmode) {
ui->listOfModes->item(i)->setSelected(true);
}
@@ -910,7 +1079,7 @@ void TaskAttacher::onRefName3(const QString& text)
onRefName(text, 2);
}
void TaskAttacher::onRefName4(const QString &text)
void TaskAttacher::onRefName4(const QString& text)
{
onRefName(text, 3);
}
@@ -920,7 +1089,7 @@ bool TaskAttacher::getFlip() const
return ui->checkBoxFlip->isChecked();
}
void TaskAttacher::changeEvent(QEvent *e)
void TaskAttacher::changeEvent(QEvent* e)
{
TaskBox::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
@@ -958,11 +1127,11 @@ void TaskAttacher::changeEvent(QEvent *e)
void TaskAttacher::visibilityAutomation(bool opening_not_closing)
{
auto defvisfunc = [] (bool opening_not_closing,
const std::string &postfix,
Gui::ViewProviderDocumentObject* vp,
App::DocumentObject *editObj,
const std::string& editSubName) {
auto defvisfunc = [](bool opening_not_closing,
const std::string& postfix,
Gui::ViewProviderDocumentObject* vp,
App::DocumentObject* editObj,
const std::string& editSubName) {
if (opening_not_closing) {
QString code = QString::fromLatin1(
"import Show\n"
@@ -981,19 +1150,19 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing)
"\t\tif len(tvObj.AttachmentSupport) > 0:\n"
"\t\t\t_tv_%4.show([lnk[0] for lnk in tvObj.AttachmentSupport])\n"
"del(tvObj)"
).arg(
QString::fromLatin1(Gui::Command::getObjectCmd(vp->getObject()).c_str()),
QString::fromLatin1(Gui::Command::getObjectCmd(editObj).c_str()),
QString::fromLatin1(editSubName.c_str()),
QString::fromLatin1(postfix.c_str()));
Gui::Command::runCommand(Gui::Command::Gui,code.toLatin1().constData());
).arg(
QString::fromLatin1(Gui::Command::getObjectCmd(vp->getObject()).c_str()),
QString::fromLatin1(Gui::Command::getObjectCmd(editObj).c_str()),
QString::fromLatin1(editSubName.c_str()),
QString::fromLatin1(postfix.c_str()));
Gui::Command::runCommand(Gui::Command::Gui, code.toLatin1().constData());
}
else if (!postfix.empty()) {
QString code = QString::fromLatin1(
"_tv_%1.restore()\n"
"del(_tv_%1)"
).arg(QString::fromLatin1(postfix.c_str()));
Gui::Command::runCommand(Gui::Command::Gui,code.toLatin1().constData());
).arg(QString::fromLatin1(postfix.c_str()));
Gui::Command::runCommand(Gui::Command::Gui, code.toLatin1().constData());
}
};
@@ -1009,18 +1178,19 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing)
return;
auto editDoc = Gui::Application::Instance->editDocument();
App::DocumentObject *editObj = ViewProvider->getObject();
App::DocumentObject* editObj = ViewProvider->getObject();
std::string editSubName;
auto sels = Gui::Selection().getSelection(nullptr, Gui::ResolveMode::NoResolve, true);
if(!sels.empty() && sels[0].pResolvedObject
&& sels[0].pResolvedObject->getLinkedObject()==editObj)
if (!sels.empty() && sels[0].pResolvedObject
&& sels[0].pResolvedObject->getLinkedObject() == editObj)
{
editObj = sels[0].pObject;
editSubName = sels[0].SubName;
} else {
ViewProviderDocumentObject *editVp = nullptr;
}
else {
ViewProviderDocumentObject* editVp = nullptr;
if (editDoc) {
editDoc->getInEdit(&editVp,&editSubName);
editDoc->getInEdit(&editVp, &editSubName);
if (editVp)
editObj = editVp->getObject();
}
@@ -1029,7 +1199,7 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing)
try {
visAutoFunc(opening_not_closing, ObjectName, ViewProvider, editObj, editSubName);
}
catch (const Base::Exception &e){
catch (const Base::Exception& e) {
e.ReportException();
}
catch (const Py::Exception&) {
@@ -1043,7 +1213,7 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing)
objName.swap(ObjectName);
visAutoFunc(opening_not_closing, objName, nullptr, nullptr, std::string());
}
catch (Base::Exception &e) {
catch (Base::Exception& e) {
e.ReportException();
}
}
@@ -1054,14 +1224,14 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing)
// TaskDialog
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TaskDlgAttacher::TaskDlgAttacher(Gui::ViewProviderDocumentObject *ViewProvider, bool createBox)
: TaskDialog(),ViewProvider(ViewProvider), parameter(nullptr)
TaskDlgAttacher::TaskDlgAttacher(Gui::ViewProviderDocumentObject* ViewProvider, bool createBox)
: TaskDialog(), ViewProvider(ViewProvider), parameter(nullptr)
{
assert(ViewProvider);
setDocumentName(ViewProvider->getDocument()->getDocument()->getName());
if(createBox) {
parameter = new TaskAttacher(ViewProvider, nullptr, QString(), tr("Attachment"));
if (createBox) {
parameter = new TaskAttacher(ViewProvider, nullptr, QString(), tr("Attachment"));
Content.push_back(parameter);
}
}
@@ -1096,11 +1266,11 @@ bool TaskDlgAttacher::accept()
//DeepSOIC: changed this to heavily rely on dialog constantly updating feature properties
//if (pcAttach->AttachmentOffset.isTouched()){
Base::Placement plm = pcAttach->AttachmentOffset.getValue();
double yaw, pitch, roll;
plm.getRotation().getYawPitchRoll(yaw,pitch,roll);
Gui::cmdAppObjectArgs(obj, "AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))",
plm.getPosition().x, plm.getPosition().y, plm.getPosition().z, yaw, pitch, roll);
Base::Placement plm = pcAttach->AttachmentOffset.getValue();
double yaw, pitch, roll;
plm.getRotation().getYawPitchRoll(yaw, pitch, roll);
Gui::cmdAppObjectArgs(obj, "AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))",
plm.getPosition().x, plm.getPosition().y, plm.getPosition().z, yaw, pitch, roll);
//}
Gui::cmdAppObjectArgs(obj, "MapReversed = %s", pcAttach->MapReversed.getValue() ? "True" : "False");

View File

@@ -113,6 +113,15 @@ private:
void updateRefButton(int idx);
void updateAttachmentOffsetUI();
void findCorrectObjAndSubInThisContext(App::DocumentObject*& obj, std::string& sub);
void handleInitialSelection();
struct SubAndObjName {
std::string objName;
std::string subName;
};
void addToReference(SubAndObjName pair);
void addToReference(const std::vector<SubAndObjName >& pairs);
/**
* @brief updateListOfModes Fills the mode list with modes that apply to
* current set of references. Maintains selection when possible.

View File

@@ -113,6 +113,10 @@ void ViewProviderAttachExtension::extensionSetupContextMenu(QMenu* menu, QObject
void ViewProviderAttachExtension::showAttachmentEditor()
{
if (Gui::Control().activeDialog()) {
Gui::Control().closeDialog();
}
// See PropertyEnumAttacherItem::openTask()
Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog();
TaskDlgAttacher* task;

View File

@@ -44,7 +44,6 @@ public:
void extensionUpdateData(const App::Property*) override;
void extensionSetupContextMenu(QMenu*, QObject*, const char*) override;
private:
void showAttachmentEditor();
};

View File

@@ -0,0 +1,94 @@
/***************************************************************************
* Copyright (c) 2024 Ondsel (PL Boyer) <development@ondsel.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <App/Document.h>
#include <App/DocumentObjectGroup.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include "ViewProviderDatum.h"
using namespace PartGui;
PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderLine, Gui::ViewProviderLine)
ViewProviderLine::ViewProviderLine()
{
PartGui::ViewProviderAttachExtension::initExtension(this);
}
bool ViewProviderLine::doubleClicked()
{
showAttachmentEditor();
return true;
}
PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderPlane, Gui::ViewProviderPlane)
ViewProviderPlane::ViewProviderPlane()
{
PartGui::ViewProviderAttachExtension::initExtension(this);
}
bool ViewProviderPlane::doubleClicked()
{
showAttachmentEditor();
return true;
}
PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderPoint, Gui::ViewProviderPoint)
ViewProviderPoint::ViewProviderPoint()
{
PartGui::ViewProviderAttachExtension::initExtension(this);
}
bool ViewProviderPoint::doubleClicked()
{
showAttachmentEditor();
return true;
}
PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderLCS, Gui::ViewProviderCoordinateSystem)
ViewProviderLCS::ViewProviderLCS()
{
PartGui::ViewProviderAttachExtension::initExtension(this);
}
bool ViewProviderLCS::doubleClicked()
{
showAttachmentEditor();
return true;
}

View File

@@ -0,0 +1,84 @@
/***************************************************************************
* Copyright (c) 2024 Ondsel (PL Boyer) <development@ondsel.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef PARTGUI_ViewProviderDatum_H
#define PARTGUI_ViewProviderDatum_H
#include <Gui/ViewProviderLine.h>
#include <Gui/ViewProviderPlane.h>
#include <Gui/ViewProviderPoint.h>
#include <Gui/ViewProviderCoordinateSystem.h>
#include <QCoreApplication>
#include <Mod/Part/Gui/ViewProviderAttachExtension.h>
namespace PartGui {
class PartGuiExport ViewProviderLine : public Gui::ViewProviderLine, PartGui::ViewProviderAttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderLine);
public:
ViewProviderLine();
~ViewProviderLine() override = default;
bool doubleClicked() override;
};
class PartGuiExport ViewProviderPlane : public Gui::ViewProviderPlane, PartGui::ViewProviderAttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderPlane);
public:
ViewProviderPlane();
~ViewProviderPlane() override = default;
bool doubleClicked() override;
};
class PartGuiExport ViewProviderPoint : public Gui::ViewProviderPoint, PartGui::ViewProviderAttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderPoint);
public:
ViewProviderPoint();
~ViewProviderPoint() override = default;
bool doubleClicked() override;
};
class PartGuiExport ViewProviderLCS : public Gui::ViewProviderCoordinateSystem, PartGui::ViewProviderAttachExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderLCS);
public:
ViewProviderLCS();
~ViewProviderLCS() override = default;
bool doubleClicked() override;
};
} // namespace PartGui
#endif // PARTGUI_ViewProviderDatum_H

View File

@@ -37,6 +37,7 @@ void WorkbenchManipulator::modifyMenuBar([[maybe_unused]] Gui::MenuItem* menuBar
void WorkbenchManipulator::modifyToolBars(Gui::ToolBarItem* toolBar)
{
addSelectionFilter(toolBar);
addDatums(toolBar);
}
void WorkbenchManipulator::modifyDockWindows([[maybe_unused]] Gui::DockWindowItems* dockWindow)
@@ -71,3 +72,18 @@ void WorkbenchManipulator::addSelectionFilter(Gui::ToolBarItem* toolBar)
}
}
}
void WorkbenchManipulator::addDatums(Gui::ToolBarItem* toolBar)
{
if (auto view = toolBar->findItem("Structure")) {
auto add = new Gui::ToolBarItem(); // NOLINT
add->setCommand("Part_Datums");
auto item = view->findItem("Std_Group");
if (item) {
view->insertItem(item, add);
}
else {
view->appendItem(add);
}
}
}

View File

@@ -54,6 +54,7 @@ protected:
private:
static void addSectionCut(Gui::MenuItem* menuBar);
static void addSelectionFilter(Gui::ToolBarItem* toolBar);
static void addDatums(Gui::ToolBarItem* toolBar);
};
} // namespace PartGui

View File

@@ -219,7 +219,9 @@ bool Body::isAllowed(const App::DocumentObject *obj)
//obj->isDerivedFrom<Part::FeaturePython>() // trouble with this line on Windows!? Linker fails to find getClassTypeId() of the Part::FeaturePython...
//obj->isDerivedFrom<Part::Feature>()
// allow VarSets for parameterization
obj->isDerivedFrom<App::VarSet>()
obj->isDerivedFrom<App::VarSet>() ||
obj->isDerivedFrom<App::DatumElement>() ||
obj->isDerivedFrom<App::LocalCoordinateSystem>()
);
}

View File

@@ -1357,9 +1357,10 @@ void ProfileBased::getAxis(const App::DocumentObject * pcReferenceAxis, const st
}
if (pcReferenceAxis->isDerivedFrom<App::Line>()) {
const App::Line* line = static_cast<const App::Line*>(pcReferenceAxis);
base = Base::Vector3d(0, 0, 0);
line->Placement.getValue().multVec(Base::Vector3d(1, 0, 0), dir);
auto* line = static_cast<const App::Line*>(pcReferenceAxis);
Base::Placement plc = line->Placement.getValue();
base = plc.getPosition();
plc.getRotation().multVec(Base::Vector3d(0, 0, 1), dir);
verifyAxisFunc(checkAxis, sketchplane, gp_Dir(dir.x, dir.y, dir.z));
return;

View File

@@ -28,6 +28,7 @@
# include <BRep_Builder.hxx>
# include <BRepBuilderAPI_MakeEdge.hxx>
# include <BRepBuilderAPI_MakeFace.hxx>
# include <BRepBuilderAPI_MakeVertex.hxx>
#endif
#include <unordered_map>
@@ -194,6 +195,10 @@ void ShapeBinder::getFilteredReferences(const App::PropertyLinkSubList* prop,
obj = plane;
break;
}
if (auto point = dynamic_cast<App::Point*>(it)) {
obj = point;
break;
}
}
}
}
@@ -244,6 +249,13 @@ Part::TopoShape ShapeBinder::buildShapeFromReferences(App::GeoFeature* obj, std:
shape.setPlacement(obj->Placement.getValue());
return shape;
}
else if (obj->isDerivedFrom<App::Point>()) {
gp_Pnt point;
BRepBuilderAPI_MakeVertex mkPoint(point);
Part::TopoShape shape(mkPoint.Shape());
shape.setPlacement(obj->Placement.getValue());
return shape;
}
return TopoDS_Shape();
}

View File

@@ -148,12 +148,12 @@ bool ReferenceSelection::allowOrigin(PartDesign::Body *body, App::OriginGroupExt
if (fits) { // check that it actually belongs to the chosen body or part
try { // here are some throwers
if (body) {
if (body->getOrigin ()->hasObject (pObj) ) {
if (body->hasObject(pObj, true) ) {
return true;
}
}
else if (originGroup ) {
if (originGroup->getOrigin()->hasObject(pObj)) {
if (originGroup->hasObject(pObj, true)) {
return true;
}
}

View File

@@ -342,11 +342,11 @@ bool ViewProviderBody::canDropObjects() const
{
// if the BaseFeature property is marked as hidden or read-only then
// it's not allowed to modify it.
PartDesign::Body* body = getObject<PartDesign::Body>();
if (body->BaseFeature.testStatus(App::Property::Status::Hidden))
return false;
if (body->BaseFeature.testStatus(App::Property::Status::ReadOnly))
auto* body = getObject<PartDesign::Body>();
if (body->BaseFeature.testStatus(App::Property::Status::Hidden)
|| body->BaseFeature.testStatus(App::Property::Status::ReadOnly)) {
return false;
}
return true;
}
@@ -355,7 +355,15 @@ bool ViewProviderBody::canDropObject(App::DocumentObject* obj) const
if (obj->isDerivedFrom<App::VarSet>()) {
return true;
}
if (!obj->isDerivedFrom(Part::Feature::getClassTypeId())) {
else if (obj->isDerivedFrom(App::DatumElement::getClassTypeId())) {
// accept only datums that are not part of a LCS.
auto* lcs = static_cast<App::DatumElement*>(obj)->getLCS();
return !lcs;
}
else if (obj->isDerivedFrom(App::LocalCoordinateSystem::getClassTypeId())) {
return !obj->isDerivedFrom(App::Origin::getClassTypeId());
}
else if (!obj->isDerivedFrom(Part::Feature::getClassTypeId())) {
return false;
}
else if (PartDesign::Body::findBodyOf(obj)) {
@@ -375,8 +383,10 @@ bool ViewProviderBody::canDropObject(App::DocumentObject* obj) const
void ViewProviderBody::dropObject(App::DocumentObject* obj)
{
PartDesign::Body* body = getObject<PartDesign::Body>();
if (obj->isDerivedFrom<Part::Part2DObject>()) {
auto* body = getObject<PartDesign::Body>();
if (obj->isDerivedFrom<Part::Part2DObject>()
|| obj->isDerivedFrom<App::DatumElement>()
|| obj->isDerivedFrom<App::LocalCoordinateSystem>()) {
body->addObject(obj);
}
else if (PartDesign::Body::isAllowed(obj) && PartDesignGui::isFeatureMovable(obj)) {

View File

@@ -2,22 +2,21 @@
* Copyright (c) 2013 Jan Rheinlaender *
* <jrheinlaender@users.sourceforge.net> *
* *
* 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/>. *
* *
***************************************************************************/

View File

@@ -2,22 +2,21 @@
* Copyright (c) 2013 Jan Rheinlaender *
* <jrheinlaender@users.sourceforge.net> *
* *
* 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/>. *
* *
***************************************************************************/

View File

@@ -380,14 +380,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const
root->insertItem(item, part);
part->setCommand("&Part Design");
// datums
Gui::MenuItem* datums = new Gui::MenuItem;
datums->setCommand("Create a datum");
*datums << "PartDesign_Point"
<< "PartDesign_Line"
<< "PartDesign_Plane";
// additives
Gui::MenuItem* additives = new Gui::MenuItem;
additives->setCommand("Create an additive feature");
@@ -429,8 +421,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const
*part << "PartDesign_Body"
<< "Separator"
<< datums
<< "PartDesign_CoordinateSystem"
<< "PartDesign_ShapeBinder"
<< "PartDesign_SubShapeBinder"
<< "PartDesign_Clone"
@@ -490,8 +480,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Sketcher_ValidateSketch"
<< "Part_CheckGeometry"
<< "PartDesign_SubShapeBinder"
<< "PartDesign_Clone"
<< "PartDesign_CompDatums";
<< "PartDesign_Clone";
part = new Gui::ToolBarItem(root);
part->setCommand("Part Design Modeling");

View File

@@ -338,17 +338,17 @@ class DocumentBasicCases(unittest.TestCase):
res = obj.getSubObject("X_Axis", retType=2)
self.assertEqual(
res[1].multVec(FreeCAD.Vector(1, 0, 0)).getAngle(FreeCAD.Vector(1, 0, 0)), 0.0
res[1].multVec(FreeCAD.Vector(0, 0, 1)).getAngle(FreeCAD.Vector(1, 0, 0)), 0.0
)
res = obj.getSubObject("Y_Axis", retType=2)
self.assertEqual(
res[1].multVec(FreeCAD.Vector(1, 0, 0)).getAngle(FreeCAD.Vector(0, 1, 0)), 0.0
res[1].multVec(FreeCAD.Vector(0, 0, 1)).getAngle(FreeCAD.Vector(0, 1, 0)), 0.0
)
res = obj.getSubObject("Z_Axis", retType=2)
self.assertEqual(
res[1].multVec(FreeCAD.Vector(1, 0, 0)).getAngle(FreeCAD.Vector(0, 0, 1)), 0.0
res[1].multVec(FreeCAD.Vector(0, 0, 1)).getAngle(FreeCAD.Vector(0, 0, 1)), 0.0
)
res = obj.getSubObject("XY_Plane", retType=2)