Sketcher: add reverse mapping correction to Carbon Copy (#25745)
* Sketcher: add reverse mapping correction to Carbon Copy * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replace M_PI with std::numbers::pi * Replace vector initialization with Base::Vector3d::UnitX/UnitY, apply suggestions from code review Co-authored-by: Kacper Donat <kadet1090@gmail.com> * Fix guard clause logic, apply suggestions from code review * Replace std::numbers::pi with pi via `using std::numbers` * Fix issues reported by the linter --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kacper Donat <kadet1090@gmail.com>
This commit is contained in:
@@ -7064,6 +7064,8 @@ bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity)
|
||||
|
||||
int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
{
|
||||
using std::numbers::pi;
|
||||
|
||||
// no need to check input data validity as this is an sketchobject managed operation.
|
||||
Base::StateLocker lock(managedoperation, true);
|
||||
|
||||
@@ -7096,6 +7098,11 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
newVals.reserve(vals.size() + svals.size());
|
||||
newcVals.reserve(cvals.size() + scvals.size());
|
||||
|
||||
const Base::Vector3d& origin = this->Placement.getValue().getPosition();
|
||||
const Base::Rotation& rotation = this->Placement.getValue().getRotation();
|
||||
const Base::Vector3d axisH = rotation.multVec(Base::Vector3d::UnitX);
|
||||
const Base::Vector3d axisV = rotation.multVec(Base::Vector3d::UnitY);
|
||||
|
||||
std::map<int, int> extMap;
|
||||
if (psObj->ExternalGeo.getSize() > 1) {
|
||||
int i = -1;
|
||||
@@ -7185,8 +7192,26 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
solverNeedsUpdate = true;
|
||||
}
|
||||
|
||||
auto applyGeometryFlipCorrection = [xinv, yinv, origin, axisV, axisH]
|
||||
(Part::Geometry* geoNew) {
|
||||
if (!xinv && !yinv) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xinv) {
|
||||
geoNew->mirror(origin, axisV);
|
||||
}
|
||||
if (yinv) {
|
||||
geoNew->mirror(origin, axisH);
|
||||
}
|
||||
};
|
||||
|
||||
for (std::vector<Part::Geometry*>::const_iterator it = svals.begin(); it != svals.end(); ++it) {
|
||||
Part::Geometry* geoNew = (*it)->copy();
|
||||
if (xinv || yinv) {
|
||||
// corrections for flipped geometry
|
||||
applyGeometryFlipCorrection(geoNew);
|
||||
}
|
||||
generateId(geoNew);
|
||||
if (construction && !geoNew->is<Part::GeomPoint>()) {
|
||||
GeometryFacade::setConstruction(geoNew, true);
|
||||
@@ -7194,6 +7219,65 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
newVals.push_back(geoNew);
|
||||
}
|
||||
|
||||
auto applyConstraintFlipCorrection = [xinv, yinv]
|
||||
(Sketcher::Constraint* newConstr) {
|
||||
if (!xinv && !yinv) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DistanceX, DistanceY
|
||||
if ((xinv && newConstr->Type == Sketcher::DistanceX) ||
|
||||
(yinv && newConstr->Type == Sketcher::DistanceY)) {
|
||||
if (newConstr->First == newConstr->Second) {
|
||||
std::swap(newConstr->FirstPos, newConstr->SecondPos);
|
||||
} else{
|
||||
newConstr->setValue(-newConstr->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Angle
|
||||
if (newConstr->Type == Sketcher::Angle) {
|
||||
auto normalizeAngle = [](double angleDeg) {
|
||||
while (angleDeg > pi) angleDeg -= pi * 2.0;
|
||||
while (angleDeg <= -pi) angleDeg += pi * 2.0;
|
||||
return angleDeg;
|
||||
};
|
||||
|
||||
if (xinv && yinv) { // rotation 180 degrees around normal axis
|
||||
if (newConstr->First ==-1 || newConstr->Second == -1
|
||||
|| newConstr->First == -2 || newConstr->Second == -2
|
||||
|| newConstr->Second == GeoEnum::GeoUndef) {
|
||||
// angle to horizontal or vertical axis
|
||||
newConstr->setValue(normalizeAngle(newConstr->getValue() + pi));
|
||||
}
|
||||
else {
|
||||
// angle between two sketch entities
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
else if (xinv) { // rotation 180 degrees around vertical axis
|
||||
if (newConstr->First == -1 || newConstr->Second == -1 || newConstr->Second == GeoEnum::GeoUndef) {
|
||||
// angle to horizontal axis
|
||||
newConstr->setValue(normalizeAngle(pi - newConstr->getValue()));
|
||||
}
|
||||
else {
|
||||
// angle between two sketch entities or angle to vertical axis
|
||||
newConstr->setValue(normalizeAngle(-newConstr->getValue()));
|
||||
}
|
||||
}
|
||||
else if (yinv) { // rotation 180 degrees around horizontal axis
|
||||
if (newConstr->First == -2 || newConstr->Second == -2) {
|
||||
// angle to vertical axis
|
||||
newConstr->setValue(normalizeAngle(pi - newConstr->getValue()));
|
||||
}
|
||||
else {
|
||||
// angle between two sketch entities or angle to horizontal axis
|
||||
newConstr->setValue(normalizeAngle(-newConstr->getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (std::vector<Sketcher::Constraint*>::const_iterator it = scvals.begin(); it != scvals.end();
|
||||
++it) {
|
||||
Sketcher::Constraint* newConstr = (*it)->copy();
|
||||
@@ -7211,6 +7295,11 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
if ((*it)->Third < -2 && (*it)->Third != GeoEnum::GeoUndef)
|
||||
newConstr->Third -= (nextextgeoid - 2);
|
||||
|
||||
if (xinv || yinv) {
|
||||
// corrections for flipped constraints
|
||||
applyConstraintFlipCorrection(newConstr);
|
||||
}
|
||||
|
||||
newcVals.push_back(newConstr);
|
||||
}
|
||||
|
||||
@@ -7225,6 +7314,62 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
// ViewProvider::UpdateData is triggered.
|
||||
Geometry.touch();
|
||||
|
||||
auto makeCorrectedExpressionString = [xinv, yinv]
|
||||
(const Sketcher::Constraint* constr, const std::string expr)
|
||||
-> std::string {
|
||||
if (!xinv && !yinv) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
// DistanceX, DistanceY
|
||||
if ((xinv && constr->Type == Sketcher::DistanceX) ||
|
||||
(yinv && constr->Type == Sketcher::DistanceY)) {
|
||||
if (constr->First == constr->Second) {
|
||||
return expr;
|
||||
} else{
|
||||
return "-(" + expr + ")";
|
||||
}
|
||||
}
|
||||
|
||||
// Angle
|
||||
if (constr->Type == Sketcher::Angle) {
|
||||
if (xinv && yinv) { // rotation 180 degrees around normal axis
|
||||
if (constr->First ==-1 || constr->Second == -1
|
||||
|| constr->First == -2 || constr->Second == -2
|
||||
|| constr->Second == GeoEnum::GeoUndef) {
|
||||
// angle to horizontal or vertical axis
|
||||
return "(" + expr + ") + 180 deg";
|
||||
}
|
||||
else {
|
||||
// angle between two sketch entities
|
||||
// do nothing
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
else if (xinv) { // rotation 180 degrees around vertical axis
|
||||
if (constr->First == -1 || constr->Second == -1 || constr->Second == GeoEnum::GeoUndef) {
|
||||
// angle to horizontal axis
|
||||
return "180 deg - (" + expr + ")";
|
||||
}
|
||||
else {
|
||||
// angle between two sketch entities or angle to vertical axis
|
||||
return "-(" + expr + ")";
|
||||
}
|
||||
}
|
||||
else if (yinv) { // rotation 180 degrees around horizontal axis
|
||||
if (constr->First == -2 || constr->Second == -2) {
|
||||
// angle to vertical axis
|
||||
return "180 deg - (" + expr + ")";
|
||||
}
|
||||
else {
|
||||
// angle between two sketch entities or angle to horizontal axis
|
||||
return "-(" + expr + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
};
|
||||
|
||||
int sourceid = 0;
|
||||
for (std::vector<Sketcher::Constraint*>::const_iterator it = scvals.begin(); it != scvals.end();
|
||||
++it, nextcid++, sourceid++) {
|
||||
@@ -7235,19 +7380,22 @@ int SketchObject::carbonCopy(App::DocumentObject* pObj, bool construction)
|
||||
App::ObjectIdentifier spath;
|
||||
std::shared_ptr<App::Expression> expr;
|
||||
std::string scname = (*it)->Name;
|
||||
std::string sref;
|
||||
if (App::ExpressionParser::isTokenAnIndentifier(scname)) {
|
||||
spath = App::ObjectIdentifier(psObj->Constraints)
|
||||
<< App::ObjectIdentifier::SimpleComponent(scname);
|
||||
expr = std::shared_ptr<App::Expression>(App::Expression::parse(
|
||||
this, spath.getDocumentObjectName().getString() + spath.toString()));
|
||||
sref = spath.getDocumentObjectName().getString() + spath.toString();
|
||||
}
|
||||
else {
|
||||
spath = psObj->Constraints.createPath(sourceid);
|
||||
expr = std::shared_ptr<App::Expression>(
|
||||
App::Expression::parse(this,
|
||||
spath.getDocumentObjectName().getString()
|
||||
+ std::string(1, '.') + spath.toString()));
|
||||
sref = spath.getDocumentObjectName().getString()
|
||||
+ std::string(1, '.') + spath.toString();
|
||||
}
|
||||
if (xinv || yinv) {
|
||||
// corrections for flipped expressions
|
||||
sref = makeCorrectedExpressionString((*it), sref);
|
||||
}
|
||||
expr = std::shared_ptr<App::Expression>(App::Expression::parse(this, sref));
|
||||
setExpression(Constraints.createPath(nextcid), std::move(expr));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user