Merge pull request #23192 from tetektoza/fix/22253_fix_losing_expression_during_sketcher_tools_usage
Sketcher: Copy expressions when rotating/moving geometry
This commit is contained in:
@@ -112,6 +112,8 @@ SET(SketcherGui_SRCS
|
||||
TaskSketcherValidation.h
|
||||
ShortcutListener.cpp
|
||||
ShortcutListener.h
|
||||
SketcherTransformationExpressionHelper.cpp
|
||||
SketcherTransformationExpressionHelper.h
|
||||
EditModeInformationOverlayCoinConverter.cpp
|
||||
EditModeInformationOverlayCoinConverter.h
|
||||
EditModeGeometryCoinConverter.cpp
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "DrawSketchDefaultWidgetController.h"
|
||||
#include "DrawSketchControllableHandler.h"
|
||||
#include "SketcherTransformationExpressionHelper.h"
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
@@ -143,10 +144,18 @@ private:
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Rotate geometries"));
|
||||
|
||||
expressionHelper.storeOriginalExpressions(sketchgui->getSketchObject(), listOfGeoIds);
|
||||
|
||||
createShape(false);
|
||||
|
||||
commandAddShapeGeometryAndConstraints();
|
||||
|
||||
expressionHelper.copyExpressionsToNewConstraints(sketchgui->getSketchObject(),
|
||||
listOfGeoIds,
|
||||
ShapeGeometry.size(),
|
||||
numberOfCopies,
|
||||
1);
|
||||
|
||||
if (deleteOriginal) {
|
||||
deleteOriginalGeos();
|
||||
}
|
||||
@@ -237,6 +246,8 @@ private:
|
||||
double length, startAngle, endAngle, totalAngle, individualAngle;
|
||||
int numberOfCopies;
|
||||
|
||||
SketcherTransformationExpressionHelper expressionHelper;
|
||||
|
||||
void deleteOriginalGeos()
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "DrawSketchDefaultWidgetController.h"
|
||||
#include "DrawSketchControllableHandler.h"
|
||||
#include "SketcherTransformationExpressionHelper.h"
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define SKETCHERGUI_DrawSketchHandlerTranslate_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <map>
|
||||
|
||||
#include <Base/Tools.h>
|
||||
|
||||
@@ -38,6 +39,7 @@
|
||||
|
||||
#include "DrawSketchDefaultWidgetController.h"
|
||||
#include "DrawSketchControllableHandler.h"
|
||||
#include "SketcherTransformationExpressionHelper.h"
|
||||
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
@@ -115,10 +117,18 @@ private:
|
||||
try {
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Translate geometries"));
|
||||
|
||||
expressionHelper.storeOriginalExpressions(sketchgui->getSketchObject(), listOfGeoIds);
|
||||
|
||||
createShape(false);
|
||||
|
||||
commandAddShapeGeometryAndConstraints();
|
||||
|
||||
expressionHelper.copyExpressionsToNewConstraints(sketchgui->getSketchObject(),
|
||||
listOfGeoIds,
|
||||
ShapeGeometry.size(),
|
||||
numberOfCopies,
|
||||
secondNumberOfCopies);
|
||||
|
||||
if (deleteOriginal) {
|
||||
deleteOriginalGeos();
|
||||
}
|
||||
@@ -227,6 +237,8 @@ private:
|
||||
bool deleteOriginal, cloneConstraints;
|
||||
int numberOfCopies, secondNumberOfCopies;
|
||||
|
||||
SketcherTransformationExpressionHelper expressionHelper;
|
||||
|
||||
void deleteOriginalGeos()
|
||||
{
|
||||
std::stringstream stream;
|
||||
@@ -427,6 +439,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
std::list<Gui::InputHint> getToolHints() const override
|
||||
{
|
||||
|
||||
174
src/Mod/Sketcher/Gui/SketcherTransformationExpressionHelper.cpp
Normal file
174
src/Mod/Sketcher/Gui/SketcherTransformationExpressionHelper.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 The FreeCAD Project Association AISBL *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include "SketcherTransformationExpressionHelper.h"
|
||||
|
||||
#include <Gui/Command.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
using namespace SketcherGui;
|
||||
|
||||
void SketcherTransformationExpressionHelper::storeOriginalExpressions(
|
||||
Sketcher::SketchObject* sketchObject,
|
||||
const std::vector<int>& listOfGeoIds)
|
||||
{
|
||||
if (!sketchObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<Sketcher::Constraint*>& vals = sketchObject->Constraints.getValues();
|
||||
|
||||
originalExpressions.clear();
|
||||
|
||||
for (auto& geoId : listOfGeoIds) {
|
||||
for (size_t i = 0; i < vals.size(); i++) {
|
||||
const auto& cstr = vals[i];
|
||||
if (cstr->isDriving && cstr->isDimensional()
|
||||
&& (cstr->First == geoId
|
||||
|| (cstr->Second == geoId && cstr->Type != Sketcher::Radius
|
||||
&& cstr->Type != Sketcher::Diameter && cstr->Type != Sketcher::Weight))) {
|
||||
|
||||
App::ObjectIdentifier spath =
|
||||
sketchObject->Constraints.createPath(static_cast<int>(i));
|
||||
App::PropertyExpressionEngine::ExpressionInfo expr_info =
|
||||
sketchObject->getExpression(spath);
|
||||
|
||||
if (expr_info.expression) {
|
||||
// map expression to geoid as a key
|
||||
originalExpressions[geoId] =
|
||||
std::shared_ptr<App::Expression>(expr_info.expression->copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherTransformationExpressionHelper::copyExpressionsToNewConstraints(
|
||||
Sketcher::SketchObject* sketchObject,
|
||||
const std::vector<int>& listOfGeoIds,
|
||||
size_t shapeGeometrySize,
|
||||
int numberOfCopies,
|
||||
int secondNumberOfCopies)
|
||||
{
|
||||
// apply stored expressions to new constraints, but bail out if we haven't stored anything
|
||||
if (originalExpressions.empty() || !sketchObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string sketchObj = Gui::Command::getObjectCmd(sketchObject);
|
||||
const std::vector<Sketcher::Constraint*>& vals = sketchObject->Constraints.getValues();
|
||||
|
||||
CopyCalculationParams params =
|
||||
calculateCopyParams(sketchObject, listOfGeoIds, shapeGeometrySize, numberOfCopies);
|
||||
for (size_t i = 0; i < vals.size(); i++) {
|
||||
const auto& cstr = vals[i];
|
||||
if (!cstr->isDriving || !cstr->isDimensional()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to find and apply a matching expression for this constraint
|
||||
bool expressionApplied = false;
|
||||
for (const auto& exprPair : originalExpressions) {
|
||||
int originalGeoId = exprPair.first;
|
||||
int originalIndex = indexOfGeoId(listOfGeoIds, originalGeoId);
|
||||
|
||||
if (originalIndex >= 0) {
|
||||
expressionApplied = tryApplyExpressionToConstraint(cstr,
|
||||
i,
|
||||
originalIndex,
|
||||
params,
|
||||
secondNumberOfCopies,
|
||||
exprPair.second,
|
||||
sketchObj);
|
||||
|
||||
if (expressionApplied) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherTransformationExpressionHelper::clear()
|
||||
{
|
||||
originalExpressions.clear();
|
||||
}
|
||||
|
||||
bool SketcherTransformationExpressionHelper::hasStoredExpressions() const
|
||||
{
|
||||
return !originalExpressions.empty();
|
||||
}
|
||||
|
||||
SketcherTransformationExpressionHelper::CopyCalculationParams
|
||||
SketcherTransformationExpressionHelper::calculateCopyParams(Sketcher::SketchObject* sketchObject,
|
||||
const std::vector<int>& listOfGeoIds,
|
||||
size_t shapeGeometrySize,
|
||||
int numberOfCopies) const
|
||||
{
|
||||
CopyCalculationParams params;
|
||||
params.firstCurveCreated =
|
||||
sketchObject->getHighestCurveIndex() + 1 - static_cast<int>(shapeGeometrySize);
|
||||
params.size = static_cast<int>(listOfGeoIds.size());
|
||||
params.numberOfCopiesToMake = numberOfCopies == 0 ? 1 : numberOfCopies;
|
||||
return params;
|
||||
}
|
||||
|
||||
bool SketcherTransformationExpressionHelper::tryApplyExpressionToConstraint(
|
||||
const Sketcher::Constraint* cstr,
|
||||
size_t constraintIndex,
|
||||
int originalIndex,
|
||||
const CopyCalculationParams& params,
|
||||
int secondNumberOfCopies,
|
||||
const std::shared_ptr<App::Expression>& expression,
|
||||
const std::string& sketchObj) const
|
||||
{
|
||||
// check all copies of this geometry as we assign them the same expression
|
||||
for (int k = 0; k < secondNumberOfCopies; k++) {
|
||||
for (int copy = 1; copy <= params.numberOfCopiesToMake; copy++) {
|
||||
int expectedNewGeoId = params.firstCurveCreated + originalIndex
|
||||
+ params.size * (copy - 1) + params.size * params.numberOfCopiesToMake * k;
|
||||
|
||||
// if this constraint references our copied geometry, apply the expression
|
||||
if (constraintReferencesGeometry(cstr, expectedNewGeoId)) {
|
||||
Gui::Command::doCommand(Gui::Command::Doc,
|
||||
"%s.setExpression('Constraints[%d]', '%s')",
|
||||
sketchObj.c_str(),
|
||||
static_cast<int>(constraintIndex),
|
||||
expression->toString().c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SketcherTransformationExpressionHelper::constraintReferencesGeometry(
|
||||
const Sketcher::Constraint* cstr,
|
||||
int geoId) const
|
||||
{
|
||||
return cstr->First == geoId
|
||||
|| (cstr->Second == geoId && cstr->Type != Sketcher::Radius
|
||||
&& cstr->Type != Sketcher::Diameter && cstr->Type != Sketcher::Weight);
|
||||
}
|
||||
107
src/Mod/Sketcher/Gui/SketcherTransformationExpressionHelper.h
Normal file
107
src/Mod/Sketcher/Gui/SketcherTransformationExpressionHelper.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 The FreeCAD Project Association AISBL *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SKETCHERGUI_SketcherTransformationExpressionHelper_H
|
||||
#define SKETCHERGUI_SketcherTransformationExpressionHelper_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <App/Expression.h>
|
||||
#include <App/PropertyExpressionEngine.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
namespace SketcherGui
|
||||
{
|
||||
|
||||
/** @brief Helper class for preserving expressions during sketch transformations
|
||||
*
|
||||
* This class provides functionality to preserve expressions when transforming
|
||||
* sketch geometries (translate, rotate, scale, etc.). It stores expressions
|
||||
* from original constraints and applies them to new constraints after transformation.
|
||||
*/
|
||||
class SketcherGuiExport SketcherTransformationExpressionHelper
|
||||
{
|
||||
public:
|
||||
/** @brief Store expressions from constraints affecting the given geometry list
|
||||
*
|
||||
* @param sketchObject The sketch object containing the constraints
|
||||
* @param listOfGeoIds List of geometry IDs that are being transformed
|
||||
*/
|
||||
void storeOriginalExpressions(Sketcher::SketchObject* sketchObject,
|
||||
const std::vector<int>& listOfGeoIds);
|
||||
|
||||
/** @brief Apply stored expressions to new constraints after transformation
|
||||
*
|
||||
* @param sketchObject The sketch object containing the new constraints
|
||||
* @param listOfGeoIds Original list of geometry IDs that were transformed
|
||||
* @param shapeGeometrySize Number of new geometries created
|
||||
* @param numberOfCopies Number of copies made (0 means single operation on the original
|
||||
* geometry)
|
||||
* @param secondNumberOfCopies Number of rows for array operations
|
||||
*/
|
||||
void copyExpressionsToNewConstraints(Sketcher::SketchObject* sketchObject,
|
||||
const std::vector<int>& listOfGeoIds,
|
||||
size_t shapeGeometrySize,
|
||||
int numberOfCopies,
|
||||
int secondNumberOfCopies);
|
||||
|
||||
void clear();
|
||||
bool hasStoredExpressions() const;
|
||||
|
||||
private:
|
||||
struct CopyCalculationParams
|
||||
{
|
||||
int firstCurveCreated;
|
||||
int size;
|
||||
int numberOfCopiesToMake;
|
||||
};
|
||||
|
||||
/// calculate parameters needed for copy operations
|
||||
CopyCalculationParams calculateCopyParams(Sketcher::SketchObject* sketchObject,
|
||||
const std::vector<int>& listOfGeoIds,
|
||||
size_t shapeGeometrySize,
|
||||
int numberOfCopies) const;
|
||||
|
||||
/// try to apply an expression to a constraint if it matches copied geometry
|
||||
bool tryApplyExpressionToConstraint(const Sketcher::Constraint* cstr,
|
||||
size_t constraintIndex,
|
||||
int originalIndex,
|
||||
const CopyCalculationParams& params,
|
||||
int secondNumberOfCopies,
|
||||
const std::shared_ptr<App::Expression>& expression,
|
||||
const std::string& sketchObj) const;
|
||||
|
||||
/// check if a constraint references the specified geometry ID
|
||||
bool constraintReferencesGeometry(const Sketcher::Constraint* cstr, int geoId) const;
|
||||
|
||||
// original geo id to expression mapping
|
||||
std::map<int, std::shared_ptr<App::Expression>> originalExpressions;
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
#endif // SKETCHERGUI_SketcherTransformationExpressionHelper_H
|
||||
Reference in New Issue
Block a user