From 89c7c98ec9edffa2ae3f5216707fb87784f4ec4c Mon Sep 17 00:00:00 2001 From: azv Date: Thu, 13 Jun 2019 11:36:52 +0300 Subject: [PATCH] Update projected coordinate axes when changing one coordinate plane to another. --- src/SketchPlugin/CMakeLists.txt | 1 + src/SketchPlugin/SketchPlugin_Sketch.cpp | 139 +++++++++++++++++- src/SketchPlugin/SketchPlugin_Sketch.h | 10 ++ .../Test/TestChangeSketchPlane3.py | 63 ++++++++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/SketchPlugin/Test/TestChangeSketchPlane3.py diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index f611de955..d72e0c56b 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -198,6 +198,7 @@ ADD_UNIT_TESTS( TestArcBehavior.py TestChangeSketchPlane1.py TestChangeSketchPlane2.py + TestChangeSketchPlane3.py TestConstraintAngle.py TestConstraintCoincidence.py TestConstraintCollinear.py diff --git a/src/SketchPlugin/SketchPlugin_Sketch.cpp b/src/SketchPlugin/SketchPlugin_Sketch.cpp index 2f51ae2da..0daeb330d 100644 --- a/src/SketchPlugin/SketchPlugin_Sketch.cpp +++ b/src/SketchPlugin/SketchPlugin_Sketch.cpp @@ -20,9 +20,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -249,6 +251,35 @@ bool SketchPlugin_Sketch::isSub(ObjectPtr theObject) const } +static bool isOrigin(const GeomPointPtr& thePoint, const double theTolerance) +{ + return fabs(thePoint->x()) < theTolerance && + fabs(thePoint->y()) < theTolerance && + fabs(thePoint->z()) < theTolerance; +} + +static bool isCoordinateAxis(const GeomDirPtr& theDir, const double theTolerance) +{ + return fabs(theDir->x() - 1.0) < theTolerance || fabs(theDir->x() + 1.0) < theTolerance || + fabs(theDir->y() - 1.0) < theTolerance || fabs(theDir->y() + 1.0) < theTolerance || + fabs(theDir->z() - 1.0) < theTolerance || fabs(theDir->z() + 1.0) < theTolerance; +} + +static bool isCoordinatePlane(const GeomAx3Ptr& thePlane) +{ + static const double THE_TOLERANCE = 1.e-7; + if (!thePlane) + return false; + + GeomPointPtr anOrigin = thePlane->origin(); + GeomDirPtr aNormal = thePlane->normal(); + GeomDirPtr aDirX = thePlane->dirX(); + + return isOrigin(anOrigin, THE_TOLERANCE) && + isCoordinateAxis(aNormal, THE_TOLERANCE) && + isCoordinateAxis(aDirX, THE_TOLERANCE); +} + void SketchPlugin_Sketch::attributeChanged(const std::string& theID) { if (theID == SketchPlugin_SketchEntity::EXTERNAL_ID()) { AttributeSelectionPtr aSelAttr = selection(SketchPlugin_SketchEntity::EXTERNAL_ID()); @@ -299,14 +330,28 @@ void SketchPlugin_Sketch::attributeChanged(const std::string& theID) { } } } else if (theID == NORM_ID() || theID == DIRX_ID() || theID == ORIGIN_ID()) { + // check if current and previous sketch planes are coordinate planes and they are different + GeomAx3Ptr aCurPlane; + bool areCoordPlanes = false; + if (isPlaneSet()) { + aCurPlane = coordinatePlane(); + areCoordPlanes = isCoordinatePlane(aCurPlane) && isCoordinatePlane(myPlane); + } + // send all sub-elements are also updated: all entities become created on different plane static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); std::list aSubs = data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->list(); std::list::iterator aSub = aSubs.begin(); for(; aSub != aSubs.end(); aSub++) { - if (aSub->get()) + if (aSub->get()) { + if (areCoordPlanes) + updateCoordinateAxis(*aSub, aCurPlane); + ModelAPI_EventCreator::get()->sendUpdated(*aSub, anUpdateEvent); + } } + if (aCurPlane) + myPlane = aCurPlane; } } @@ -433,3 +478,95 @@ bool SketchPlugin_Sketch::removeLinksToExternal() document()->removeFeature(*anIt); return true; } + + +static ObjectPtr findAxis(GeomShapePtr theAxisToCompare, + ObjectPtr theOX, + ObjectPtr theOY, + ObjectPtr theOZ) +{ + if (theAxisToCompare) { + ObjectPtr anAxes[] = { theOX, theOY, theOZ }; + for (int i = 0; i < 3; ++i) { + ResultPtr anAx = std::dynamic_pointer_cast(anAxes[i]); + if (anAx && theAxisToCompare->isEqual(anAx->shape())) + return anAxes[i]; + } + } + return ObjectPtr(); +} + +static ObjectPtr findAxis(ObjectPtr theAxisToCompare, + ObjectPtr theOX, + ObjectPtr theOY, + ObjectPtr theOZ) +{ + if (theAxisToCompare == theOX) + return theOX; + else if (theAxisToCompare == theOY) + return theOY; + else if (theAxisToCompare == theOZ) + return theOZ; + // nothing helped, search by shape + ResultPtr anAxis = std::dynamic_pointer_cast(theAxisToCompare); + return findAxis(anAxis ? anAxis->shape() : GeomShapePtr(), theOX, theOY, theOZ); +} + +GeomShapePtr axisOnNewPlane(ObjectPtr theAxis, GeomAx3Ptr theOldPlane, GeomAx3Ptr theNewPlane) +{ + ResultPtr anAxis = std::dynamic_pointer_cast(theAxis); + if (!anAxis) + return GeomShapePtr(); + + GeomEdgePtr anAxisEdge = anAxis->shape()->edge(); + GeomLinePtr anAxisLine = anAxisEdge->line(); + GeomDirPtr anAxisDir = anAxisLine->direction(); + + double aFirstParam, aLastParam; + anAxisEdge->getRange(aFirstParam, aLastParam); + + if (theOldPlane->dirX()->isParallel(anAxisDir)) + anAxisDir = theNewPlane->dirX(); + else if (theOldPlane->dirY()->isParallel(anAxisDir)) + anAxisDir = theNewPlane->dirY(); + else if (theOldPlane->normal()->isParallel(anAxisDir)) + anAxisDir = theNewPlane->normal(); + + GeomPointPtr aFirstPoint(new GeomAPI_Pnt(aFirstParam * anAxisDir->x(), + aFirstParam * anAxisDir->y(), + aFirstParam * anAxisDir->z())); + GeomPointPtr aLastPoint(new GeomAPI_Pnt(aLastParam * anAxisDir->x(), + aLastParam * anAxisDir->y(), + aLastParam * anAxisDir->z())); + return GeomAlgoAPI_EdgeBuilder::line(aFirstPoint, aLastPoint); +} + +void SketchPlugin_Sketch::updateCoordinateAxis(ObjectPtr theSub, GeomAx3Ptr thePlane) +{ + FeaturePtr aFeature = ModelAPI_Feature::feature(theSub); + if (!aFeature) + return; + + DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument(); + ObjectPtr anOX = aRootDoc->objectByName(ModelAPI_ResultConstruction::group(), "OX"); + ObjectPtr anOY = aRootDoc->objectByName(ModelAPI_ResultConstruction::group(), "OY"); + ObjectPtr anOZ = aRootDoc->objectByName(ModelAPI_ResultConstruction::group(), "OZ"); + + AttributeSelectionPtr anExtFeature; + if (aFeature->getKind() == SketchPlugin_Projection::ID()) + anExtFeature = aFeature->selection(SketchPlugin_Projection::EXTERNAL_FEATURE_ID()); + else if (aFeature->getKind() == SketchPlugin_IntersectionPoint::ID()) + anExtFeature = aFeature->selection(SketchPlugin_IntersectionPoint::EXTERNAL_FEATURE_ID()); + else + return; + + ObjectPtr aContext = anExtFeature->context(); + GeomShapePtr aShape = anExtFeature->value(); + if (!aShape) { // selected object is a construction + ObjectPtr anAxis = findAxis(aContext, anOX, anOY, anOZ); + GeomShapePtr aNewAxis = axisOnNewPlane(anAxis, myPlane, thePlane); + anAxis = findAxis(aNewAxis, anOX, anOY, anOZ); + if (anAxis) + anExtFeature->setValue(anAxis, aShape); + } +} diff --git a/src/SketchPlugin/SketchPlugin_Sketch.h b/src/SketchPlugin/SketchPlugin_Sketch.h index d94893db0..124ff670c 100644 --- a/src/SketchPlugin/SketchPlugin_Sketch.h +++ b/src/SketchPlugin/SketchPlugin_Sketch.h @@ -177,6 +177,10 @@ class SketchPlugin_Sketch : public ModelAPI_CompositeFeature, public GeomAPI_ICu std::shared_ptr aNorm = std::dynamic_pointer_cast( aData->attribute(NORM_ID())); + if (!aNorm->isInitialized() || !aX->isInitialized() || + aNorm->dir()->cross(aX->dir())->squareModulus() < 1.e-14) + return std::shared_ptr(); + return std::shared_ptr(new GeomAPI_Ax3(aC->pnt(), aX->dir(), aNorm->dir())); } @@ -258,6 +262,12 @@ private: /// Substitute all links to external objects by newly created features. /// \return \c true, if all links updated. bool removeLinksToExternal(); + + /// Update projected coordinate axes + void updateCoordinateAxis(ObjectPtr theSub, std::shared_ptr thePlane); + +private: + std::shared_ptr myPlane; }; #endif diff --git a/src/SketchPlugin/Test/TestChangeSketchPlane3.py b/src/SketchPlugin/Test/TestChangeSketchPlane3.py new file mode 100644 index 000000000..6746b9562 --- /dev/null +++ b/src/SketchPlugin/Test/TestChangeSketchPlane3.py @@ -0,0 +1,63 @@ +# Copyright (C) 2019 CEA/DEN, EDF R&D +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from SketchAPI import * +from salome.shaper import model +import math + +def checkSketchLine(theLine, theX1, theY1, theX2, theY2, theTolerance = 1.e-7): + assert(math.fabs(theLine.startPoint().x() - theX1) < theTolerance) + assert(math.fabs(theLine.startPoint().y() - theY1) < theTolerance) + assert(math.fabs(theLine.endPoint().x() - theX2) < theTolerance) + assert(math.fabs(theLine.endPoint().y() - theY2) < theTolerance) + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +ParamD1 = model.addParameter(Part_1_doc, "D1", "80") +ParamD2 = model.addParameter(Part_1_doc, "D2", "30") +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, 80, 30, 0) +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_2 = SketchProjection_1.createdFeature() +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result()) +SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False) +SketchLine_3 = SketchProjection_2.createdFeature() +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.result()) +SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_1.startPoint(), SketchAPI_Line(SketchLine_2).startPoint(), "D1", True) +SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_1.endPoint(), SketchAPI_Line(SketchLine_2).startPoint(), "D2", True) +model.do() +model.end() +model.checkSketch(Sketch_1, 0) +checkSketchLine(SketchLine_1, 0, ParamD1.value(), ParamD2.value(), 0) + +model.begin() +Sketch_1.setPlane(model.selection("FACE", "PartSet/YOZ")) +model.end() +model.checkSketch(Sketch_1, 0) +checkSketchLine(SketchLine_1, 0, ParamD1.value(), ParamD2.value(), 0) + +model.begin() +Sketch_1.setPlane(model.selection("FACE", "PartSet/XOZ")) +model.end() +model.checkSketch(Sketch_1, 0) +checkSketchLine(SketchLine_1, 0, ParamD1.value(), ParamD2.value(), 0) + +assert(model.checkPythonDump()) -- 2.39.2