From 26d09c86761e841a0cb93418fd91d817f5e26763 Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 20 Mar 2015 10:10:10 +0300 Subject: [PATCH] Fillet constraint is implemented. Small changes according Mirror constraint were made. --- src/SketchPlugin/CMakeLists.txt | 2 + .../SketchPlugin_ConstraintFillet.cpp | 91 ++++ .../SketchPlugin_ConstraintFillet.h | 54 +++ src/SketchPlugin/SketchPlugin_Plugin.cpp | 4 + src/SketchPlugin/plugin-Sketch.xml | 16 +- src/SketchSolver/SketchSolver_Constraint.cpp | 21 + .../SketchSolver_ConstraintGroup.cpp | 419 +++++++++++++++++- .../SketchSolver_ConstraintGroup.h | 17 + src/SketchSolver/SketchSolver_Solver.h | 2 + 9 files changed, 623 insertions(+), 3 deletions(-) create mode 100644 src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp create mode 100644 src/SketchPlugin/SketchPlugin_ConstraintFillet.h diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 1a70246b2..298457133 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -27,6 +27,7 @@ SET(PROJECT_HEADERS SketchPlugin_ConstraintEqual.h SketchPlugin_ConstraintTangent.h SketchPlugin_ConstraintMirror.h + SketchPlugin_ConstraintFillet.h SketchPlugin_ShapeValidator.h SketchPlugin_Validators.h SketchPlugin_ResultValidators.h @@ -54,6 +55,7 @@ SET(PROJECT_SOURCES SketchPlugin_ConstraintEqual.cpp SketchPlugin_ConstraintTangent.cpp SketchPlugin_ConstraintMirror.cpp + SketchPlugin_ConstraintFillet.cpp SketchPlugin_ShapeValidator.cpp SketchPlugin_Validators.cpp SketchPlugin_ResultValidators.cpp diff --git a/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp new file mode 100644 index 000000000..4b7b4cff7 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp @@ -0,0 +1,91 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> + +// File: SketchPlugin_ConstraintFillet.cpp +// Created: 19 Mar 2015 +// Author: Artem ZHIDKOV + +#include "SketchPlugin_ConstraintFillet.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet() +{ +} + +void SketchPlugin_ConstraintFillet::initAttributes() +{ + data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::type()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::type()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::type()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::type()); + // initialize attribute not applicable for user + data()->attribute(SketchPlugin_Constraint::ENTITY_C())->setInitialized(); +} + +void SketchPlugin_ConstraintFillet::execute() +{ + std::shared_ptr aData = data(); + // Check the base objects are initialized + AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::ENTITY_A())); + AttributeRefAttrPtr aBaseB = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::ENTITY_B())); + if (!aBaseA->isInitialized() || !aBaseB->isInitialized() || + !aBaseA->isObject() || !aBaseB->isObject()) + return; + // Check the fillet shapes is not initialized yet + AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::ENTITY_C())); + if (aRefListOfFillet->size() > 0) + return; + // Obtain features for the base objects + FeaturePtr aFeatureA, aFeatureB; + ResultConstructionPtr aRC = + std::dynamic_pointer_cast(aBaseA->object()); + if (aRC) aFeatureA = aRC->document()->feature(aRC); + aRC = std::dynamic_pointer_cast(aBaseB->object()); + if (aRC) aFeatureB = aRC->document()->feature(aRC); + if (!aFeatureA || !aFeatureB) + return; + + // Create list of objects composing a fillet + // copy aFeatureA + FeaturePtr aNewFeature = sketch()->addFeature(aFeatureA->getKind()); + aFeatureA->data()->copyTo(aNewFeature->data()); + aRefListOfFillet->append(aNewFeature); + // copy aFeatureB + aNewFeature = sketch()->addFeature(aFeatureB->getKind()); + aFeatureB->data()->copyTo(aNewFeature->data()); + aRefListOfFillet->append(aNewFeature); + // create filleting arc + aNewFeature = sketch()->addFeature(SketchPlugin_Arc::ID()); + aNewFeature->attribute(SketchPlugin_Arc::CENTER_ID())->setInitialized(); + aNewFeature->attribute(SketchPlugin_Arc::START_ID())->setInitialized(); + aNewFeature->attribute(SketchPlugin_Arc::END_ID())->setInitialized(); + aRefListOfFillet->append(aNewFeature); + aRefListOfFillet->setInitialized(); +} + +AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious) +{ + if (!sketch()) + return thePrevious; + + AISObjectPtr anAIS = thePrevious; + /// TODO: Equal constraint presentation should be put here + return anAIS; +} + + diff --git a/src/SketchPlugin/SketchPlugin_ConstraintFillet.h b/src/SketchPlugin/SketchPlugin_ConstraintFillet.h new file mode 100644 index 000000000..5b7cd4946 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_ConstraintFillet.h @@ -0,0 +1,54 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> + +// File: SketchPlugin_ConstraintFillet.h +// Created: 19 Mar 2015 +// Author: Artem ZHIDKOV + +#ifndef SketchPlugin_ConstraintFillet_H_ +#define SketchPlugin_ConstraintFillet_H_ + +#include "SketchPlugin.h" +#include +#include "SketchPlugin_ConstraintBase.h" + +/** \class SketchPlugin_ConstraintFillet + * \ingroup Plugins + * \brief Feature for creation of a new constraint filleting two objects which have coincident point + * + * This constraint has three attributes: + * SketchPlugin_Constraint::ENTITY_A() and SketchPlugin_Constraint::ENTITY_B() for the filleting objects; + * SketchPlugin_Constraint::VALUE() contains radius of filleting circular arc + * + * Also the constraint has attribute SketchPlugin_Constraint::ENTITY_C() + * which contains created list objects forming the fillet + */ +class SketchPlugin_ConstraintFillet : public SketchPlugin_ConstraintBase +{ + public: + /// Fillet constraint kind + inline static const std::string& ID() + { + static const std::string MY_CONSTRAINT_FILLET_ID("SketchConstraintFillet"); + return MY_CONSTRAINT_FILLET_ID; + } + /// \brief Returns the kind of a feature + SKETCHPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = SketchPlugin_ConstraintFillet::ID(); + return MY_KIND; + } + + /// \brief Creates a new part document if needed + SKETCHPLUGIN_EXPORT virtual void execute(); + + /// \brief Request for initialization of data model of the feature: adding all attributes + SKETCHPLUGIN_EXPORT virtual void initAttributes(); + + /// Returns the AIS preview + SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious); + + /// \brief Use plugin manager for features creation + SketchPlugin_ConstraintFillet(); +}; + +#endif diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index fbf1235ce..9bcc299bf 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,8 @@ FeaturePtr SketchPlugin_Plugin::createFeature(string theFeatureID) return FeaturePtr(new SketchPlugin_ConstraintTangent); } else if (theFeatureID == SketchPlugin_ConstraintMirror::ID()) { return FeaturePtr(new SketchPlugin_ConstraintMirror); + } else if (theFeatureID == SketchPlugin_ConstraintFillet::ID()) { + return FeaturePtr(new SketchPlugin_ConstraintFillet); } // feature of such kind is not found return FeaturePtr(); @@ -173,6 +176,7 @@ std::shared_ptr SketchPlugin_Plugin aMsg->setState(SketchPlugin_ConstraintEqual::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_ConstraintTangent::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_ConstraintMirror::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_ConstraintFillet::ID(), aHasSketchPlane); } } return aMsg; diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index b4a7ab35b..cccba9845 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -5,7 +5,7 @@ + + + + + + + + + + + + diff --git a/src/SketchSolver/SketchSolver_Constraint.cpp b/src/SketchSolver/SketchSolver_Constraint.cpp index aed82cec5..4ee164e1d 100644 --- a/src/SketchSolver/SketchSolver_Constraint.cpp +++ b/src/SketchSolver/SketchSolver_Constraint.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -286,6 +287,26 @@ const int& SketchSolver_Constraint::getType( return getType(); } + if (aConstraintKind.compare(SketchPlugin_ConstraintFillet::ID()) == 0) { + int aNbAttrs = 0; + for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++) { + AttributeRefListPtr anAttrRefList = std::dynamic_pointer_cast( + aConstrData->attribute(SketchPlugin_Constraint::ATTRIBUTE(indAttr))); + if (anAttrRefList) + myAttributesList[aNbAttrs++] = SketchPlugin_Constraint::ATTRIBUTE(indAttr); + else { + std::shared_ptr anAttr = + aConstrData->attribute(SketchPlugin_Constraint::ATTRIBUTE(indAttr)); + AttrType aType = typeOfAttribute(anAttr); + if (aType == LINE || aType == ARC) + myAttributesList[aNbAttrs++] = SketchPlugin_Constraint::ATTRIBUTE(indAttr); + } + } + if (aNbAttrs == 3) + myType = SLVS_C_FILLET; + return getType(); + } + /// \todo Implement other kind of constraints return getType(); diff --git a/src/SketchSolver/SketchSolver_ConstraintGroup.cpp b/src/SketchSolver/SketchSolver_ConstraintGroup.cpp index 4340f7018..f8470401a 100644 --- a/src/SketchSolver/SketchSolver_ConstraintGroup.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintGroup.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include +#include #include #include #include @@ -250,6 +252,8 @@ bool SketchSolver_ConstraintGroup::changeConstraint( return changeRigidConstraint(theConstraint); if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) return changeMirrorConstraint(theConstraint); + if (theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID()) + return changeFilletConstraint(theConstraint); } // Search this constraint in the current group to update it @@ -514,7 +518,7 @@ bool SketchSolver_ConstraintGroup::changeRigidConstraint( aConstrIter = myConstraints.begin() + aConstrPos; myNeedToSolve = true; } else { - myConstraintMap[theConstraint] = std::vector(); + myConstraintMap[theConstraint] = std::vector(); // To avoid SolveSpace problems: // * if the circle is rigid, we will fix its center and radius; @@ -658,7 +662,7 @@ bool SketchSolver_ConstraintGroup::changeMirrorConstraint( if (aBaseList.size() != aMirroredList.size()) return false; - myConstraintMap[theConstraint] = std::vector(); + myConstraintMap[theConstraint] = std::vector(); FeaturePtr aBaseFeature, aMirrorFeature; ResultConstructionPtr aRC; @@ -677,6 +681,8 @@ bool SketchSolver_ConstraintGroup::changeMirrorConstraint( return false; Slvs_hEntity aBaseEnt = changeEntityFeature(aBaseFeature); Slvs_hEntity aMirrorEnt = changeEntityFeature(aMirrorFeature); + // Make aMirrorEnt parameters to be symmetric with aBaseEnt + makeMirrorEntity(aBaseEnt, aMirrorEnt, aMirrorLineEnt); if (aBaseFeature->getKind() == SketchPlugin_Point::ID()) { Slvs_Constraint aConstraint = Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, @@ -735,6 +741,328 @@ bool SketchSolver_ConstraintGroup::changeMirrorConstraint( myEntities[aMirrorLinePos].point[1], SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN); myConstraints.push_back(aRigidEnd); myConstraintMap[theConstraint].push_back(aRigidEnd.h); + + // Add temporary constraints for initial objects to be unchanged + for (aBaseIter = aBaseList.begin(); aBaseIter != aBaseList.end(); aBaseIter++) { + aRC = std::dynamic_pointer_cast(*aBaseIter); + aBaseFeature = aRC ? aRC->document()->feature(aRC) : + std::dynamic_pointer_cast(*aBaseIter); + if (!aBaseFeature) continue; + std::list aPoints = aBaseFeature->data()->attributes(GeomDataAPI_Point2D::type()); + std::list::iterator anIt = aPoints.begin(); + for ( ; anIt != aPoints.end(); anIt++) { + // Arcs are fixed by center and start points only (to avoid solving errors in SolveSpace) + if (aBaseFeature->getKind() == SketchPlugin_Arc::ID() && + (*anIt)->id() == SketchPlugin_Arc::END_ID()) + continue; + addTemporaryConstraintWhereDragged(*anIt); + } + } + } + return true; +} + +// ============================================================================ +// Function: changeFilletConstraint +// Class: SketchSolver_ConstraintGroup +// Purpose: create/update the "Fillet" constraint in the group +// ============================================================================ +bool SketchSolver_ConstraintGroup::changeFilletConstraint( + std::shared_ptr theConstraint) +{ + DataPtr aConstrData = theConstraint->data(); + + // Search this constraint in the current group to update it + ConstraintMap::const_iterator aConstrMapIter = myConstraintMap.find(theConstraint); + std::vector::iterator aConstrIter; + if (aConstrMapIter != myConstraintMap.end()) { + int aConstrPos = Search(aConstrMapIter->second.front(), myConstraints); + aConstrIter = myConstraints.begin() + aConstrPos; + } + + // Get constraint type and verify the constraint parameters are correct + SketchSolver_Constraint aConstraint(theConstraint); + int aConstrType = aConstraint.getType(); + if (aConstrType == SLVS_C_UNKNOWN) + return false; + const std::vector& aConstraintAttributes = aConstraint.getAttributes(); + + // Obtain hEntity for basic objects of fillet + Slvs_hEntity aBaseObject[2]; + FeaturePtr aBaseFeature[2]; + for (unsigned int indAttr = 0; indAttr < 2; indAttr++) { + AttributeRefAttrPtr aConstrAttr = std::dynamic_pointer_cast( + aConstrData->attribute(aConstraintAttributes[indAttr])); + if (!aConstrAttr) + return false; + if (aConstrAttr->isObject() && aConstrAttr->object()) { + ResultConstructionPtr aRC = std::dynamic_pointer_cast( + aConstrAttr->object()); + if (!aRC) + return false; + std::shared_ptr aDoc = aRC->document(); + aBaseFeature[indAttr] = aDoc->feature(aRC); + } + aBaseObject[indAttr] = aConstrAttr->isObject() ? + changeEntityFeature(aBaseFeature[indAttr]) : changeEntity(aConstrAttr->attr()); + } + // Check the base entities have a coincident point + int aBaseObjInd[2] = { + Search(aBaseObject[0], myEntities), + Search(aBaseObject[1], myEntities) + }; + int aShift[2] = { // shift for calculating correct start and end points for different types of objects + myEntities[aBaseObjInd[0]].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0, + myEntities[aBaseObjInd[1]].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0, + }; + Slvs_hEntity aFirstObjPoints[2] = { // indices of start and end point of first object + myEntities[aBaseObjInd[0]].point[aShift[0]], + myEntities[aBaseObjInd[0]].point[1+aShift[0]] + }; + Slvs_hEntity aSecondObjPoints[2] = { // indices of start and end point of second object + myEntities[aBaseObjInd[1]].point[aShift[1]], + myEntities[aBaseObjInd[1]].point[1+aShift[1]] + }; + bool isCoincidentFound = false; + int aBaseCoincInd[2] = {0, 0}; // indices in aFirstObjPoint and aSecondObjPoint identifying coincident points + std::vector >::iterator aCPIter = myCoincidentPoints.begin(); + for ( ; aCPIter != myCoincidentPoints.end() && !isCoincidentFound; aCPIter++) + for (int ind1 = 0; ind1 < 2 && !isCoincidentFound; ind1++) + for (int ind2 = 0; ind2 < 2 && !isCoincidentFound; ind2++) + if (aCPIter->find(aFirstObjPoints[ind1]) != aCPIter->end() && + aCPIter->find(aSecondObjPoints[ind2]) != aCPIter->end()) { + aBaseCoincInd[0] = ind1; + aBaseCoincInd[1] = ind2; + isCoincidentFound = true; + } + if (!isCoincidentFound) { + // There is no coincident points between objects. Generate error message + Events_Error::send(SketchSolver_Error::NO_COINCIDENT_POINTS(), this); + return false; + } + + // Create fillet entities + // - first object is placed on the first base + // - second object is on the second base + // - third object is a filleting arc + static const int aNbFilletEnt = 3; + Slvs_hEntity aFilletEnt[aNbFilletEnt]; + int aFilletObjInd[aNbFilletEnt]; + AttributeRefListPtr aFilletRefList = std::dynamic_pointer_cast( + aConstrData->attribute(aConstraintAttributes[2])); + if (!aFilletRefList) + return false; + std::list aFilletList = aFilletRefList->list(); + if (aFilletList.size() < aNbFilletEnt) + return false; + FeaturePtr aFilletFeature; + ResultConstructionPtr aRC; + std::list::iterator aFilIter = aFilletList.begin(); + for (int indEnt = 0; aFilIter != aFilletList.end(); aFilIter++, indEnt++) { + aRC = std::dynamic_pointer_cast(*aFilIter); + aFilletFeature = aRC ? aRC->document()->feature(aRC) : + std::dynamic_pointer_cast(*aFilIter); + if (!aFilletFeature) + return false; + aFilletEnt[indEnt] = changeEntityFeature(aFilletFeature); + aFilletObjInd[indEnt] = Search(aFilletEnt[indEnt], myEntities); + } + // At first time, for correct result, move floating points of fillet on the middle points of base objects + if (myConstraintMap.find(theConstraint) == myConstraintMap.end()) { + double anArcPoints[6]; + for (int indEnt = 0; indEnt < aNbFilletEnt - 1; indEnt++) { + int anIndShift = myEntities[aFilletObjInd[indEnt]].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0; + int aPointsPos[2] = { + Search(myEntities[aFilletObjInd[indEnt]].point[anIndShift], myEntities), + Search(myEntities[aFilletObjInd[indEnt]].point[1+anIndShift], myEntities) + }; + int aParamPos[2] = { + Search(myEntities[aPointsPos[0]].param[0], myParams), + Search(myEntities[aPointsPos[1]].param[0], myParams) + }; + int anIndex = aParamPos[aBaseCoincInd[indEnt]]; + myParams[anIndex].val = + 0.5 * (myParams[aParamPos[0]].val + myParams[aParamPos[1]].val); + myParams[1 + anIndex].val = + 0.5 * (myParams[1 + aParamPos[0]].val + myParams[1 + aParamPos[1]].val); + if (anIndShift == 1) { // place the changed point on the arc + int aCenterPos = Search(myEntities[aFilletObjInd[indEnt]].point[0], myEntities); + int aCenterParam = Search(myEntities[aCenterPos].param[0], myParams); + double x = myParams[anIndex].val - myParams[aCenterParam].val; + double y = myParams[1 + anIndex].val - myParams[1 + aCenterParam].val; + double aNorm = sqrt(x*x + y*y); + if (aNorm >= tolerance) { + double x1 = myParams[aParamPos[1-aBaseCoincInd[indEnt]]].val - myParams[aCenterParam].val; + double y1 = myParams[1 + aParamPos[1-aBaseCoincInd[indEnt]]].val - myParams[1 + aCenterParam].val; + double aRad = sqrt(x1*x1 + y1*y1); + myParams[anIndex].val = myParams[aCenterPos].val + x * aRad / aNorm; + myParams[1 + anIndex].val = myParams[1 + aCenterPos].val + y * aRad / aNorm; + } + } + anArcPoints[indEnt*2+2] = myParams[anIndex].val; + anArcPoints[indEnt*2+3] = myParams[1 + anIndex].val; + } + anArcPoints[0] = 0.5 * (anArcPoints[2] + anArcPoints[4]); + anArcPoints[1] = 0.5 * (anArcPoints[3] + anArcPoints[5]); + for (int indArcPt = 0; indArcPt < 3; indArcPt++) { + int aPtPos = Search(myEntities[aFilletObjInd[2]].point[indArcPt], myEntities); + int aParamPos = Search(myEntities[aPtPos].param[0], myParams); + myParams[aParamPos].val = anArcPoints[indArcPt * 2]; + myParams[aParamPos + 1].val = anArcPoints[indArcPt * 2 + 1]; + } + } + + // Check the fillet arc which point to be connected to + bool isArcInversed = false; // indicates that start and end points of arc should be connected to second and first object respectively + Slvs_hEntity hEnt = myEntities[aFilletObjInd[2]].point[1]; + int aPos = Search(hEnt, myEntities); + Slvs_hParam anArcStartPoint = myEntities[aPos].param[0]; + aPos = Search(anArcStartPoint, myParams); + double anArcPtCoord[2] = {myParams[aPos].val, myParams[aPos+1].val}; + double aSqDistances[2]; + int aPtInd; + for (int indEnt = 0; indEnt < aNbFilletEnt - 1; indEnt++) { + aPtInd = aBaseCoincInd[indEnt]+aShift[indEnt]; + hEnt = myEntities[aFilletObjInd[indEnt]].point[aPtInd]; + aPos = Search(hEnt, myEntities); + Slvs_hParam anObjectPoint = myEntities[aPos].param[0]; + aPos = Search(anObjectPoint, myParams); + double aPtCoord[2] = {myParams[aPos].val, myParams[aPos+1].val}; + aSqDistances[indEnt] = + (anArcPtCoord[0] - aPtCoord[0]) * (anArcPtCoord[0] - aPtCoord[0]) + + (anArcPtCoord[1] - aPtCoord[1]) * (anArcPtCoord[1] - aPtCoord[1]); + } + if (aSqDistances[1] < aSqDistances[0]) + isArcInversed = true; + + // Create list of constraints to generate fillet + std::vector aConstrList; + bool isExists = myConstraintMap.find(theConstraint) != myConstraintMap.end(); // constraint already exists + std::vector::iterator aCMapIter = + isExists ? myConstraintMap[theConstraint].begin() : aConstrList.begin(); + int aCurConstrPos = isExists ? Search(*aCMapIter, myConstraints) : 0; + for (int indEnt = 0; indEnt < aNbFilletEnt - 1; indEnt++) { + // one point of fillet object should be coincident with the point on base, non-coincident with another base object + aPtInd = 1-aBaseCoincInd[indEnt]+aShift[indEnt]; // (1-aBaseCoincInd[indEnt]) = index of non-coincident point, aShift is used to process all types of shapes + Slvs_hEntity aPtBase = myEntities[aBaseObjInd[indEnt]].point[aPtInd]; + Slvs_hEntity aPtFillet = myEntities[aFilletObjInd[indEnt]].point[aPtInd]; + if (isExists) { + myConstraints[aCurConstrPos].ptA = aPtBase; + myConstraints[aCurConstrPos].ptB = aPtFillet; + aCMapIter++; + aCurConstrPos = Search(*aCMapIter, myConstraints); + } else { + Slvs_Constraint aCoincConstr = Slvs_MakeConstraint( + ++myConstrMaxID, myID, SLVS_C_POINTS_COINCIDENT, myWorkplane.h, + 0, aPtBase, aPtFillet, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN); + myConstraints.push_back(aCoincConstr); + aConstrList.push_back(aCoincConstr.h); + } + + // another point of fillet object should be placed on the base object + Slvs_Constraint aPonCurveConstr; + int aTangentType; + if (myEntities[aFilletObjInd[indEnt]].type == SLVS_E_ARC_OF_CIRCLE) { + // centers of arcs should be coincident + aPtBase = myEntities[aBaseObjInd[indEnt]].point[0]; + aPtFillet = myEntities[aFilletObjInd[indEnt]].point[0]; + if (isExists) { + myConstraints[aCurConstrPos].ptA = aPtBase; + myConstraints[aCurConstrPos].ptB = aPtFillet; + aCMapIter++; + aCurConstrPos = Search(*aCMapIter, myConstraints); + } else { + aPonCurveConstr = Slvs_MakeConstraint( + ++myConstrMaxID, myID, SLVS_C_POINTS_COINCIDENT, myWorkplane.h, + 0, aPtBase, aPtFillet, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN); + } + aPtFillet = myEntities[aFilletObjInd[indEnt]].point[1+aBaseCoincInd[indEnt]]; // !!! will be used below + aTangentType = SLVS_C_CURVE_CURVE_TANGENT; + } else { + aPtInd = aBaseCoincInd[indEnt]; + aPtFillet = myEntities[aFilletObjInd[indEnt]].point[aPtInd]; + if (isExists) { + myConstraints[aCurConstrPos].ptA = aPtFillet; + aCMapIter++; + aCurConstrPos = Search(*aCMapIter, myConstraints); + } else { + aPonCurveConstr = Slvs_MakeConstraint( + ++myConstrMaxID, myID, SLVS_C_PT_ON_LINE, myWorkplane.h, + 0, aPtFillet, SLVS_E_UNKNOWN, aBaseObject[indEnt], SLVS_E_UNKNOWN); + } + aTangentType = SLVS_C_ARC_LINE_TANGENT; + } + if (!isExists) { + myConstraints.push_back(aPonCurveConstr); + aConstrList.push_back(aPonCurveConstr.h); + } + + // Bound point of fillet arc should be tangently coincident with a bound point of fillet object + aPtInd = 1 + (isArcInversed ? 1-indEnt : indEnt); + Slvs_hEntity aPtArc = myEntities[aFilletObjInd[2]].point[aPtInd]; + if (isExists) { + myConstraints[aCurConstrPos].ptA = aPtArc; + myConstraints[aCurConstrPos].ptB = aPtFillet; + aCMapIter++; + aCurConstrPos = Search(*aCMapIter, myConstraints); + myConstraints[aCurConstrPos].entityA = aFilletEnt[2]; + myConstraints[aCurConstrPos].entityB = aFilletEnt[indEnt]; + myConstraints[aCurConstrPos].other = (isArcInversed ? 1-indEnt : indEnt); + aCMapIter++; + aCurConstrPos = Search(*aCMapIter, myConstraints); + } else { + Slvs_Constraint aCoincConstr = Slvs_MakeConstraint( + ++myConstrMaxID, myID, SLVS_C_POINTS_COINCIDENT, myWorkplane.h, + 0, aPtArc, aPtFillet, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN); + myConstraints.push_back(aCoincConstr); + aConstrList.push_back(aCoincConstr.h); + Slvs_Constraint aTangency = Slvs_MakeConstraint( + ++myConstrMaxID, myID, aTangentType, myWorkplane.h, + 0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aFilletEnt[2], aFilletEnt[indEnt]); + aTangency.other = (isArcInversed ? 1-indEnt : indEnt); + aTangency.other2 = aTangentType == SLVS_C_CURVE_CURVE_TANGENT ? aBaseCoincInd[indEnt] : 0; + myConstraints.push_back(aTangency); + aConstrList.push_back(aTangency.h); + } + } + + // Additional constraint for fillet diameter + double aRadius = 0.0; // scalar value of the constraint + AttributeDoublePtr aDistAttr = std::dynamic_pointer_cast( + aConstrData->attribute(SketchPlugin_Constraint::VALUE())); + aRadius = aDistAttr->value(); + if (isExists) { + myConstraints[aCurConstrPos].entityA = aFilletEnt[2]; + myConstraints[aCurConstrPos].valA = aRadius * 2.0; + aCMapIter++; + } else { + Slvs_Constraint aDiamConstr = Slvs_MakeConstraint( + ++myConstrMaxID, myID, SLVS_C_DIAMETER, myWorkplane.h, aRadius * 2.0, + SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aFilletEnt[2], SLVS_E_UNKNOWN); + myConstraints.push_back(aDiamConstr); + aConstrList.push_back(aDiamConstr.h); + + myConstraintMap[theConstraint] = aConstrList; + } + + // Additional temporary constraints for base objects to be fixed + for (unsigned int indAttr = 0; indAttr < 2; indAttr++) { + if (!aBaseFeature[indAttr]) { + AttributeRefAttrPtr aConstrAttr = std::dynamic_pointer_cast( + aConstrData->attribute(aConstraintAttributes[indAttr])); + addTemporaryConstraintWhereDragged(aConstrAttr->attr()); + continue; + } + std::list anAttributes = + aBaseFeature[indAttr]->data()->attributes(GeomDataAPI_Point2D::type()); + std::list::iterator anIt = anAttributes.begin(); + for ( ; anIt != anAttributes.end(); anIt++) { + // Arc should be fixed by center and start points only (to avoid "conflicting constraints" message) + if (aBaseFeature[indAttr]->getKind() == SketchPlugin_Arc::ID() && + (*anIt)->id() == SketchPlugin_Arc::END_ID()) + continue; + addTemporaryConstraintWhereDragged(*anIt); + } } return true; } @@ -1102,6 +1430,7 @@ bool SketchSolver_ConstraintGroup::resolveConstraints() if (updateAttribute(anEntIter->first, anEntIter->second)) updateRelatedConstraints(anEntIter->first); } + updateFilletConstraints(); // unblock all features then for (anEntIter = myEntityAttrMap.begin(); anEntIter != myEntityAttrMap.end(); anEntIter++) { if (anEntIter->first->owner().get() && anEntIter->first->owner()->data().get()) @@ -1757,6 +2086,92 @@ void SketchSolver_ConstraintGroup::updateRelatedConstraintsFeature( } } +// ============================================================================ +// Function: updateFilletConstraints +// Class: SketchSolver_ConstraintGroup +// Purpose: change fillet arc to be less than 180 degree +// ============================================================================ +void SketchSolver_ConstraintGroup::updateFilletConstraints() +{ + ConstraintMap::const_iterator aConstrIter = myConstraintMap.begin(); + for (; aConstrIter != myConstraintMap.end(); aConstrIter++) + if (aConstrIter->first->getKind() == SketchPlugin_ConstraintFillet::ID()) { + AttributeRefListPtr aFilletRefList = std::dynamic_pointer_cast( + aConstrIter->first->data()->attribute(SketchPlugin_ConstraintFillet::ENTITY_C())); + if (!aFilletRefList) + return; + ObjectPtr anArcObj = aFilletRefList->object(2); + std::shared_ptr aCenter = std::dynamic_pointer_cast( + anArcObj->data()->attribute(SketchPlugin_Arc::CENTER_ID())); + std::shared_ptr aStart = std::dynamic_pointer_cast( + anArcObj->data()->attribute(SketchPlugin_Arc::START_ID())); + std::shared_ptr aEnd = std::dynamic_pointer_cast( + anArcObj->data()->attribute(SketchPlugin_Arc::END_ID())); + double aCosA = aStart->x() - aCenter->x(); + double aSinA = aStart->y() - aCenter->y(); + double aCosB = aEnd->x() - aCenter->x(); + double aSinB = aEnd->y() - aCenter->y(); + if (aCosA * aSinB - aSinA * aCosB <= 0.0) { + anArcObj->data()->blockSendAttributeUpdated(true); + double x = aStart->x(); + double y = aStart->y(); + aStart->setValue(aEnd->x(), aEnd->y()); + aEnd->setValue(x, y); + // Update constraint data + changeFilletConstraint(aConstrIter->first); + anArcObj->data()->blockSendAttributeUpdated(false); + } + } +} + +// ============================================================================ +// Function: makeMirrorEntity +// Class: SketchSolver_ConstraintGroup +// Purpose: change entities parameters to make them symmetric relating to the mirror line +// ============================================================================ +void SketchSolver_ConstraintGroup::makeMirrorEntity(const Slvs_hEntity& theBase, + const Slvs_hEntity& theMirror, + const Slvs_hEntity& theMirrorLine) +{ + Slvs_Entity aBase = myEntities[Search(theBase, myEntities)]; + Slvs_Entity aMirror = myEntities[Search(theMirror, myEntities)]; + int i = 0; + while (aBase.point[i] != 0 && aMirror.point[i] != 0) { + makeMirrorEntity(aBase.point[i], aMirror.point[i], theMirrorLine); + i++; + } + if (aBase.param[0] != 0 && aMirror.param[0] != 0) { // this is a point, copy it + Slvs_Entity aMirrorLine = myEntities[Search(theMirrorLine, myEntities)]; + std::shared_ptr aLinePoints[2]; + for (i = 0; i < 2; i++) { + Slvs_Entity aPoint = myEntities[Search(aMirrorLine.point[i], myEntities)]; + int aParamPos = Search(aPoint.param[0], myParams); + aLinePoints[i] = std::shared_ptr( + new GeomAPI_XY(myParams[aParamPos].val, myParams[1+aParamPos].val)); + } + int aBaseParamPos = Search(aBase.param[0], myParams); + int aMirrorParamPos = Search(aMirror.param[0], myParams); + std::shared_ptr aPoint = std::shared_ptr( + new GeomAPI_XY(myParams[aBaseParamPos].val, myParams[1+aBaseParamPos].val)); + + // direction of a mirror line + std::shared_ptr aDir = std::shared_ptr( + new GeomAPI_Dir2d(aLinePoints[1]->added(aLinePoints[0]->multiplied(-1.0)))); + // orthogonal direction + aDir = std::shared_ptr(new GeomAPI_Dir2d(aDir->y(), -aDir->x())); + + std::shared_ptr aVec = std::shared_ptr( + new GeomAPI_XY(aPoint->x() - aLinePoints[0]->x(), aPoint->y() - aLinePoints[0]->y())); + double aDist = aVec->dot(aDir->xy()); + std::shared_ptr aMirrorPoint = aPoint->added(aDir->xy()->multiplied(-2.0 * aDist)); + + myParams[aMirrorParamPos].val = aMirrorPoint->x(); + myParams[1+aMirrorParamPos].val = aMirrorPoint->y(); + } +} + + + // ======================================================== // ========= Auxiliary functions =============== // ======================================================== diff --git a/src/SketchSolver/SketchSolver_ConstraintGroup.h b/src/SketchSolver/SketchSolver_ConstraintGroup.h index 5816eee75..9f0e52706 100644 --- a/src/SketchSolver/SketchSolver_ConstraintGroup.h +++ b/src/SketchSolver/SketchSolver_ConstraintGroup.h @@ -72,6 +72,11 @@ class SketchSolver_ConstraintGroup * \return \c true if the constraint added or updated successfully */ bool changeMirrorConstraint(std::shared_ptr theConstraint); + /** \brief Adds or updates a fillet constraint in the group + * \param[in] theConstraint constraint to be changed + * \return \c true if the constraint added or updated successfully + */ + bool changeFilletConstraint(std::shared_ptr theConstraint); /** \brief Verifies the feature attributes are used in this group * \param[in] theFeature constraint or any other object for verification of interaction @@ -186,6 +191,9 @@ protected: bool updateAttribute(std::shared_ptr theAttribute, const Slvs_hEntity& theEntityID); + /// \brief Update arc of fillet to be less than 180 degree + void updateFilletConstraints(); + /** \brief Adds a constraint for a point which should not be changed during computations * \param[in] theEntity the base for the constraint * \param[in] theAllowToFit this flag shows that the entity may be placed into @@ -222,6 +230,15 @@ protected: */ void checkConstraintConsistence(Slvs_Constraint& theConstraint); + /** \brief Change entities parameters to make them symmetric relating to the mirror line + * \param[in] theBase entity to be mirrored + * \param[in] theMirror a mirrored object + * \param[in] theMirrorLine a mirror line + */ + void makeMirrorEntity(const Slvs_hEntity& theBase, + const Slvs_hEntity& theMirror, + const Slvs_hEntity& theMirrorLine); + private: // SolveSpace entities Slvs_hGroup myID; ///< the index of the group diff --git a/src/SketchSolver/SketchSolver_Solver.h b/src/SketchSolver/SketchSolver_Solver.h index 7079f1598..4a45b5253 100644 --- a/src/SketchSolver/SketchSolver_Solver.h +++ b/src/SketchSolver/SketchSolver_Solver.h @@ -24,6 +24,8 @@ typedef unsigned int UINT32; // Unknown constraint (for error reporting) #define SLVS_C_UNKNOWN 0 +// Fillet constraint identifier +#define SLVS_C_FILLET 100100 // Unknown entity #define SLVS_E_UNKNOWN 0 -- 2.39.2