Sketcher: assign the old geometries' GeometryId[s] to new geometries after scaling (#22263)

* Reassign facade ids after scale operation if deleting geometries

* Fix failing CI
This commit is contained in:
theo-vt
2025-07-14 11:39:03 -04:00
committed by GitHub
parent e38fe196d5
commit c8bddd2f2b
5 changed files with 118 additions and 10 deletions

View File

@@ -11631,6 +11631,24 @@ std::vector<Base::Vector3d> SketchObject::getOpenVertices() const
// SketchGeometryExtension interface
size_t setGeometryIdHelper(int GeoId, long id, std::vector<Part::Geometry*>& newVals, size_t searchOffset = 0, bool returnOnMatch = false)
{
// deep copy
for (size_t i = searchOffset; i < newVals.size(); i++) {
newVals[i] = newVals[i]->clone();
if ((int)i == GeoId) {
auto gf = GeometryFacade::getFacade(newVals[i]);
gf->setId(id);
if (returnOnMatch) {
return i;
}
}
}
return 0;
}
int SketchObject::setGeometryId(int GeoId, long id)
{
// no need to check input data validity as this is an sketchobject managed operation.
@@ -11641,18 +11659,34 @@ int SketchObject::setGeometryId(int GeoId, long id)
const std::vector<Part::Geometry*>& vals = getInternalGeometry();
std::vector<Part::Geometry*> newVals(vals);
setGeometryIdHelper(GeoId, id, newVals);
// There is not actual internal transaction going on here, however neither the geometry indices
// nor the vertices need to be updated so this is a convenient way of preventing it.
{
Base::StateLocker lock(internaltransaction, true);
this->Geometry.setValues(std::move(newVals));
}
return 0;
}
int SketchObject::setGeometryIds(std::vector<std::pair<int, long>> GeoIdsToIds)
{
Base::StateLocker lock(managedoperation, true);
std::sort(GeoIdsToIds.begin(), GeoIdsToIds.end());
size_t searchOffset = 0;
const std::vector<Part::Geometry*>& vals = getInternalGeometry();
std::vector<Part::Geometry*> newVals(vals);
// deep copy
for (size_t i = 0; i < newVals.size(); i++) {
newVals[i] = newVals[i]->clone();
if ((int)i == GeoId) {
auto gf = GeometryFacade::getFacade(newVals[i]);
gf->setId(id);
}
for (size_t i = 0; i < GeoIdsToIds.size(); ++i) {
int GeoId = GeoIdsToIds[i].first;
long id = GeoIdsToIds[i].second;
searchOffset = setGeometryIdHelper(GeoId, id, newVals, searchOffset, i != GeoIdsToIds.size()-1);
}
// There is not actual internal transaction going on here, however neither the geometry indices

View File

@@ -893,6 +893,7 @@ public:
public: // geometry extension functionalities for single element sketch object user convenience
int setGeometryId(int GeoId, long id);
int setGeometryIds(std::vector<std::pair<int, long>> GeoIdsToIds);
int getGeometryId(int GeoId, long& id) const;
protected:

View File

@@ -868,6 +868,12 @@ class SketchObject(Part2DObject):
"""
...
def setGeometryIds(GeoIdsToIds: List[Tuple[int, int]]):
"""
Sets the GeometryId of the SketchGeometryExtension of the geometries with the provided GeoIds
Expects a list of pairs (GeoId, id)
"""
def getGeometryId():
"""
Gets the GeometryId of the SketchGeometryExtension of the geometry with the provided GeoId

View File

@@ -2447,6 +2447,49 @@ PyObject* SketchObjectPy::setGeometryId(PyObject* args)
Py_Return;
}
PyObject* SketchObjectPy::setGeometryIds(PyObject* args)
{
PyObject* pyList;
// Parse arguments: list of pairs, Base::VectorPy, optional relative flag
if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &pyList)) {
return nullptr;
}
// Convert Python list to std::vector<std::pair<int, long>>
std::vector<std::pair<int, long>> geoIdsToIds;
Py_ssize_t listSize = PyList_Size(pyList);
for (Py_ssize_t i = 0; i < listSize; ++i) {
PyObject* pyPair = PyList_GetItem(pyList, i); // Borrowed reference
if (!PyTuple_Check(pyPair) || PyTuple_Size(pyPair) != 2) {
PyErr_SetString(PyExc_ValueError, "List must contain pairs (geoId, id).");
return nullptr;
}
int geoId = PyLong_AsLong(PyTuple_GetItem(pyPair, 0));
long id = PyLong_AsLong(PyTuple_GetItem(pyPair, 1));
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "Invalid geoId or id in the list.");
return nullptr;
}
geoIdsToIds.emplace_back(geoId, id);
}
// Call the C++ method
if (this->getSketchObjectPtr()->setGeometryIds(geoIdsToIds)) {
std::stringstream str;
str << "Not able to set geometry Ids of geometries with the given indices: ";
PyErr_SetString(PyExc_ValueError, str.str().c_str());
return nullptr;
}
Py_Return;
}
Py::Long SketchObjectPy::getDoF() const
{

View File

@@ -110,6 +110,7 @@ public:
if (deleteOriginal) {
deleteOriginalGeos();
reassignFacadeIds();
}
Gui::Command::commitCommand();
@@ -226,6 +227,7 @@ private:
private:
std::vector<int> listOfGeoIds;
std::vector<long> listOfFacadeIds;
Base::Vector2d referencePoint, startPoint, endPoint;
bool deleteOriginal;
bool abortOnFail; // When the scale operation is part of a larger transaction, one might want
@@ -261,6 +263,24 @@ private:
Base::Console().error("%s\n", e.what());
}
}
void reassignFacadeIds()
{
std::stringstream stream;
int geoId = getHighestCurveIndex() - listOfFacadeIds.size() + 1;
for (size_t j = 0; j < listOfFacadeIds.size() - 1; j++) {
stream << "(" << geoId << "," << listOfFacadeIds[j] << "),";
geoId++;
}
stream << "(" << geoId << "," << listOfFacadeIds.back() << ")";
try {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"setGeometryIds([%s])",
stream.str().c_str());
}
catch (const Base::Exception& e) {
Base::Console().error("%s\n", e.what());
}
}
void createShape(bool onlyeditoutline) override
{
@@ -281,7 +301,11 @@ private:
for (auto& geoId : listOfGeoIds) {
const Part::Geometry* pGeo = Obj->getGeometry(geoId);
auto geoUniquePtr = std::unique_ptr<Part::Geometry>(pGeo->copy());
long facadeId;
Obj->getGeometryId(geoId, facadeId);
listOfFacadeIds.push_back(facadeId);
auto geoUniquePtr = std::unique_ptr<Part::Geometry>(pGeo->clone());
Part::Geometry* geo = geoUniquePtr.get();
if (isCircle(*geo)) {