X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchPlugin%2FSketchPlugin_ConstraintAngle.cpp;h=cdb3309ecf47c08e50ea813d3c2e0ec5ce2730a6;hb=refs%2Fheads%2FV9_11_BR;hp=8caf3ee8984a36308348c5d646ad04a08da52d6f;hpb=6967e5c6b1993581cc0711ec4c95bb01f74224b7;p=modules%2Fshaper.git diff --git a/src/SketchPlugin/SketchPlugin_ConstraintAngle.cpp b/src/SketchPlugin/SketchPlugin_ConstraintAngle.cpp index 8caf3ee89..cdb3309ec 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintAngle.cpp +++ b/src/SketchPlugin/SketchPlugin_ConstraintAngle.cpp @@ -1,17 +1,38 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> - -// File: SketchPlugin_ConstraintAngle.cpp -// Created: 19 August 2015 -// Author: Artem ZHIDKOV +// Copyright (C) 2014-2023 CEA, EDF +// +// 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 +// #include "SketchPlugin_ConstraintAngle.h" #include +#include +#include #include #include +#include +#include +#include + +#include #include +#include #include #include #include @@ -20,11 +41,26 @@ #include #include -#include +#include +#include +#include +#include const double tolerance = 1.e-7; #define PI 3.1415926535897932 +// To support old types of GCC (less than 4.9), check the regular expressions are working +#if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) && \ + (__cplusplus >= 201402L || !defined(__GLIBCXX__) || \ + (defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \ + defined(_GLIBCXX_REGEX_STATE_LIMIT) || \ + (defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE > 4))) +#define HAVE_WORKING_REGEX 1 +#else +#define HAVE_WORKING_REGEX 0 +#endif + + /// \brief Calculate intersection point of two lines static std::shared_ptr intersect(FeaturePtr theLine1, FeaturePtr theLine2); @@ -36,13 +72,45 @@ SketchPlugin_ConstraintAngle::SketchPlugin_ConstraintAngle() void SketchPlugin_ConstraintAngle::initAttributes() { + ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators(); + data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId()); - data()->addAttribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID(), ModelAPI_AttributeDouble::typeId()); - data()->addAttribute(SketchPlugin_ConstraintAngle::TYPE_ID(), ModelAPI_AttributeInteger::typeId()); + data()->addAttribute(ANGLE_VALUE_ID(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(TYPE_ID(), ModelAPI_AttributeInteger::typeId()); + + data()->addAttribute(ANGLE_REVERSED_FIRST_LINE_ID(), ModelAPI_AttributeBoolean::typeId()); + data()->addAttribute(ANGLE_REVERSED_SECOND_LINE_ID(), ModelAPI_AttributeBoolean::typeId()); + + data()->addAttribute(LOCATION_TYPE_ID(), ModelAPI_AttributeInteger::typeId()); + aValidators->registerNotObligatory(getKind(), LOCATION_TYPE_ID()); + + data()->addAttribute(PREV_TYPE_ID(), ModelAPI_AttributeInteger::typeId()); + data()->attribute(PREV_TYPE_ID())->setIsArgument(false); + aValidators->registerNotObligatory(getKind(), PREV_TYPE_ID()); + if (attribute(TYPE_ID())->isInitialized()) + integer(PREV_TYPE_ID())->setValue(integer(TYPE_ID())->value()); + + data()->addAttribute(SELECTED_FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId()); + data()->attribute(SELECTED_FIRST_POINT_ID())->setIsArgument(false); + aValidators->registerNotObligatory(getKind(), SELECTED_FIRST_POINT_ID()); + + data()->addAttribute(SELECTED_SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId()); + data()->attribute(SELECTED_SECOND_POINT_ID())->setIsArgument(false); + aValidators->registerNotObligatory(getKind(), SELECTED_SECOND_POINT_ID()); + + AttributeIntegerPtr aVerAttr = std::dynamic_pointer_cast( + data()->addAttribute(VERSION_ID(), ModelAPI_AttributeInteger::typeId())); + aVerAttr->setIsArgument(false); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VERSION_ID()); + if (!aVerAttr->isInitialized()) { + // this is a newly created feature (not read from file), + // so, initialize the latest version + aVerAttr->setValue(THE_VERSION_1); + } } void SketchPlugin_ConstraintAngle::colorConfigInfo(std::string& theSection, std::string& theName, @@ -57,20 +125,21 @@ void SketchPlugin_ConstraintAngle::execute() { std::shared_ptr aData = data(); - std::shared_ptr anAttrA = aData->refattr(SketchPlugin_Constraint::ENTITY_A()); - std::shared_ptr anAttrB = aData->refattr(SketchPlugin_Constraint::ENTITY_B()); + AttributeRefAttrPtr anAttrA = aData->refattr(SketchPlugin_Constraint::ENTITY_A()); + AttributeRefAttrPtr anAttrB = aData->refattr(SketchPlugin_Constraint::ENTITY_B()); if (!anAttrA->isInitialized() || !anAttrB->isInitialized()) return; - AttributeDoublePtr anAttrValue = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID())); + AttributeIntegerPtr aVersion = integer(VERSION_ID()); + if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1) + updateVersion(); - if (!anAttrValue->isInitialized()) { - double anAngle = calculateAngle(); - anAttrValue->setValue(anAngle); - updateAngleValue(); - } - // the value should to be computed here, not in the getAISObject in order to change the model value + AttributeDoublePtr anAttrValue = real(ANGLE_VALUE_ID()); + if (!anAttrValue->isInitialized()) + calculateAngle(); + + // the value should to be computed here, not in the + // getAISObject in order to change the model value // inside the object transaction. This is important for creating a constraint by preselection. // The display of the presentation in this case happens after the transaction commit std::shared_ptr aFlyOutAttr = std::dynamic_pointer_cast< @@ -84,172 +153,299 @@ AISObjectPtr SketchPlugin_ConstraintAngle::getAISObject(AISObjectPtr thePrevious if (!sketch()) return thePrevious; - AISObjectPtr anAIS = SketcherPrs_Factory::angleConstraint(this, sketch()->coordinatePlane(), + AISObjectPtr anAIS = SketcherPrs_Factory::angleConstraint(this, sketch(), thePrevious); + if (anAIS.get() && !thePrevious.get()) + SketchPlugin_Tools::setDimensionColor(anAIS); return anAIS; } +// LCOV_EXCL_START +std::string SketchPlugin_ConstraintAngle::processEvent( + const std::shared_ptr& theMessage) +{ + std::string aFilledAttributeName; + + std::shared_ptr aReentrantMessage = + std::dynamic_pointer_cast(theMessage); + if (aReentrantMessage.get()) { + aFilledAttributeName = ENTITY_A(); + refattr(ENTITY_A())->setObject(aReentrantMessage->selectedObject()); + std::dynamic_pointer_cast(attribute(SELECTED_FIRST_POINT_ID())) + ->setValue(aReentrantMessage->clickedPoint()); + } + return aFilledAttributeName; +} +// LCOV_EXCL_STOP + void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID) { + if (myFlyoutUpdate) + return; + std::shared_ptr aData = data(); if (!aData) return; - FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A()); - FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B()); + + if (theID == TYPE_ID()) + updateAngleValue(); + + FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A()); + FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B()); if (!aLineA || !aLineB) return; - if (theID == SketchPlugin_Constraint::ENTITY_A() || - theID == SketchPlugin_Constraint::ENTITY_B()) { - std::shared_ptr aValueAttr = std::dynamic_pointer_cast< - ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID())); - if (!aValueAttr->isInitialized()) { // only if it is not initialized, try to compute the current value - double anAngle = calculateAngle(); - aValueAttr->setValue(anAngle); - updateAngleValue(); - } - } else if (theID == SketchPlugin_Constraint::FLYOUT_VALUE_PNT() && !myFlyoutUpdate) { - // Recalculate flyout point in local coordinates - // coordinates are calculated according to the center of shapes intersection - std::shared_ptr aFlyoutAttr = - std::dynamic_pointer_cast(attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT())); - - std::shared_ptr aData = data(); - std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); - FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A()); - FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B()); - - // Intersection of lines - std::shared_ptr anInter = intersect(aLineA, aLineB); - if (!anInter) - return; + AttributeIntegerPtr aVersion = integer(VERSION_ID()); + if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1) + updateVersion(); - myFlyoutUpdate = true; - std::shared_ptr aFlyoutDir = aFlyoutAttr->pnt()->xy()->decreased(anInter->xy()); - if (aFlyoutDir->dot(aFlyoutDir) < tolerance * tolerance) - aFlyoutAttr->setValue(aFlyoutAttr->x() + tolerance, aFlyoutAttr->y()); - myFlyoutUpdate = false; - } - else if (theID == SketchPlugin_ConstraintAngle::TYPE_ID()) { - std::shared_ptr aValueAttr = std::dynamic_pointer_cast< - ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID())); - double anAngle = calculateAngle(); - aValueAttr->setValue(anAngle); - updateAngleValue(); - } - else if (theID == SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()) { - updateAngleValue(); + if (theID == ENTITY_A() || theID == ENTITY_B() || + theID == TYPE_ID() || theID == ANGLE_VALUE_ID()) { + calculateAngle(); + } else if (theID == FLYOUT_VALUE_PNT()) { + compute(theID); } } -double SketchPlugin_ConstraintAngle::calculateAngle() +void SketchPlugin_ConstraintAngle::calculateAngle() { - double anAngle = 0.0; + // update *_REVERSED_* flags + calculateAnglePosition(); std::shared_ptr aData = data(); std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); - FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A()); - FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B()); + FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A()); + FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B()); - // Intersection of lines - std::shared_ptr anInter = intersect(aLineA, aLineB); - if (!anInter) - return anAngle; + GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID()); + GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID()); + GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID()); + GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID()); - // Start and end points of lines - std::shared_ptr aPointA1 = std::dynamic_pointer_cast( - aLineA->attribute(SketchPlugin_Line::START_ID())); - std::shared_ptr aPointA2 = std::dynamic_pointer_cast( - aLineA->attribute(SketchPlugin_Line::END_ID())); - - std::shared_ptr aPointB1 = std::dynamic_pointer_cast( - aLineB->attribute(SketchPlugin_Line::START_ID())); - std::shared_ptr aPointB2 = std::dynamic_pointer_cast( - aLineB->attribute(SketchPlugin_Line::END_ID())); - - std::shared_ptr aStartA = aPointA1->pnt(); - std::shared_ptr aEndA = aPointA2->pnt(); - std::shared_ptr aStartB = aPointB1->pnt(); - std::shared_ptr aEndB = aPointB2->pnt(); - - double aDist[2][2] = { - { anInter->distance(aStartA), anInter->distance(aEndA) }, - { anInter->distance(aStartB), anInter->distance(aEndB) } - }; - - // Directions of lines - if (aDist[0][0] > aDist[0][1]) - aEndA = aStartA; - if (aDist[1][0] > aDist[1][1]) - aEndB = aStartB; - - std::shared_ptr aDirA(new GeomAPI_Dir2d(aEndA->xy()->decreased(anInter->xy()))); - std::shared_ptr aDirB(new GeomAPI_Dir2d(aEndB->xy()->decreased(anInter->xy()))); - - double aDirAngle = aDirA->angle(aDirB); - if (aDirAngle < 0) - aDirAngle += 2.0 * PI; - anAngle = fabs(aDirAngle) * 180.0 / PI; - //anAngle = fabs(aDirA->angle(aDirB)) * 180.0 / PI; - - std::shared_ptr aTypeAttr = std::dynamic_pointer_cast< - ModelAPI_AttributeInteger>(aData->attribute(SketchPlugin_ConstraintAngle::TYPE_ID())); - SketcherPrs_Tools::AngleType anAngleType = (SketcherPrs_Tools::AngleType)(aTypeAttr->value()); - switch (anAngleType) { + std::shared_ptr aLine1(new GeomAPI_Lin2d(aStartA, aEndA)); + std::shared_ptr aLine2(new GeomAPI_Lin2d(aStartB, aEndB)); + + bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value(); + bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value(); + + AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID()); + if (!anAngleValueAttr->isInitialized()) { + std::shared_ptr anAng( + new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2)); + anAngleValueAttr->setValue(getAngleForType(fabs(anAng->angleDegree()))); + } + + std::shared_ptr anAng(new GeomAPI_Angle2d(aLine1, false, aLine2, false)); + double anAngle = anAng->angleDegree(); + + anAngle /= fabs(anAngle); + anAngle *= getAngleForType(anAngleValueAttr->value(), isReversed1, isReversed2); + + // update value of the constraint to be passed to the solver + real(SketchPlugin_Constraint::VALUE())->setValue(anAngle); +} + +void SketchPlugin_ConstraintAngle::calculateAnglePosition() +{ + if (attribute(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() && + attribute(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized()) + return; // already calculated + + DataPtr aData = data(); + FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A()); + FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B()); + + GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID()); + GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID()); + GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID()); + GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID()); + + bool isReversed1 = false; + bool isReversed2 = false; + + GeomPnt2dPtr aSelected1 = SketcherPrs_Tools::getPoint(this, SELECTED_FIRST_POINT_ID()); + GeomPnt2dPtr aSelected2 = SketcherPrs_Tools::getPoint(this, SELECTED_SECOND_POINT_ID()); + if (aSelected1 && aSelected2) { + GeomPnt2dPtr anInterPnt = intersect(aLineA, aLineB); + if (!anInterPnt) + return; + std::shared_ptr anInterXY = anInterPnt->xy(); + isReversed1 = aSelected1->xy()->decreased(anInterXY)->dot( + aEndA->xy()->decreased(aStartA->xy())) < -tolerance; + isReversed2 = aSelected2->xy()->decreased(anInterXY)->dot( + aEndB->xy()->decreased(aStartB->xy())) < -tolerance; + } + else { + // no point is selected (document opened or Python script is loaded), + // calculate basing on the value + std::shared_ptr anAng(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB)); + isReversed1 = anAng->isReversed(0); + isReversed2 = anAng->isReversed(1); + } + + // adjust reversed flags according to the angle type + AttributeIntegerPtr aTypeAttr = integer(TYPE_ID()); + if (aTypeAttr && aTypeAttr->isInitialized() && + (SketcherPrs_Tools::AngleType)(aTypeAttr->value()) == SketcherPrs_Tools::ANGLE_COMPLEMENTARY) + isReversed1 = !isReversed1; + + boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1); + boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2); +} + +// Convert angle value from the DIRECT to any given type. +static double angleForType(const double theAngle, const int theType) +{ + double anAngle = theAngle; + switch ((SketcherPrs_Tools::AngleType)theType) { case SketcherPrs_Tools::ANGLE_DIRECT: - break; - case SketcherPrs_Tools::ANGLE_SUPPLEMENTARY: - anAngle = 180 - anAngle; - break; + anAngle = theAngle; + break; + case SketcherPrs_Tools::ANGLE_COMPLEMENTARY: + anAngle = 180.0 - theAngle; + break; case SketcherPrs_Tools::ANGLE_BACKWARD: - anAngle = 360 - anAngle; - break; + anAngle = 360.0 - theAngle; + break; default: break; } return anAngle; } -void SketchPlugin_ConstraintAngle::updateAngleValue() +double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle, + bool isReversed1, + bool isReversed2) { - std::shared_ptr aValueAttr = std::dynamic_pointer_cast< - ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID())); - double anAngle = aValueAttr->value(); + double anAngle = angleForType(theAngle, integer(TYPE_ID())->value()); + if (isReversed1 != isReversed2) + anAngle = 180.0 - anAngle; + return anAngle; +} - std::shared_ptr aData = data(); - std::shared_ptr aTypeAttr = std::dynamic_pointer_cast< - ModelAPI_AttributeInteger>(aData->attribute(SketchPlugin_ConstraintAngle::TYPE_ID())); - SketcherPrs_Tools::AngleType anAngleType = (SketcherPrs_Tools::AngleType)(aTypeAttr->value()); - switch (anAngleType) { - case SketcherPrs_Tools::ANGLE_DIRECT: - break; - case SketcherPrs_Tools::ANGLE_SUPPLEMENTARY: - anAngle = 180 - anAngle; - break; - case SketcherPrs_Tools::ANGLE_BACKWARD: - anAngle = 360 - anAngle; - break; - default: - break; +#if !HAVE_WORKING_REGEX +static bool parseString(const std::wstring& theString, std::wostringstream* theResult) +{ + // skip leading spaces + size_t aLength = theString.size(); + size_t aPos = theString.find_first_not_of(L' '); + if (aPos == std::wstring::npos) + return false; + // first should be a value + if (theString[aPos] == L'-' || theString[aPos] == L'+') + theResult[1] << theString[aPos++]; + while (aPos < aLength && theString[aPos] >= L'0' && theString[aPos] <= L'9') + theResult[1] << theString[aPos++]; + if (theString[aPos] != L' ') { + if (theString[aPos] != L'.') + return false; + theResult[1] << theString[aPos++]; + while (aPos < aLength && theString[aPos] >= L'0' && theString[aPos] <= L'9') + theResult[1] << theString[aPos++]; } - aValueAttr = std::dynamic_pointer_cast< - ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_Constraint::VALUE())); - aValueAttr->setValue(anAngle); + + // next, find the sign + aPos = theString.find_first_not_of(L' ', aPos); + if (aPos == std::wstring::npos) + return false; + if (theString[aPos] == L'-' || theString[aPos] == L'+') + theResult[2] << theString[aPos++]; + + // a variable should be at the end + aPos = theString.find_first_not_of(L' ', aPos); + if (aPos == std::wstring::npos) + return false; + if (theString[aPos] != L'(' || theString.back() != L')') + return false; + theResult[3] << theString.substr(aPos + 1, aLength - aPos - 2); + + return true; } +#endif -void SketchPlugin_ConstraintAngle::move(double theDeltaX, double theDeltaY) +// Convert angle value or a text expression from one angle type to another +static void convertAngle(AttributeDoublePtr theAngle, + const int thePrevType, const int theNewType) { - std::shared_ptr aData = data(); - if (!aData->isValid()) - return; + if (theAngle->isInitialized()) { + if (theAngle->text().empty()) { + // calculate value related to the type twice: + // the first time - to return to direct angle, + // the second time - to apply new type + double aValue = angleForType(theAngle->value(), thePrevType); + aValue = angleForType(aValue, theNewType); + theAngle->setValue(aValue); + } + else { + // process the parametric value + std::wstring anAngleText = theAngle->text(); +#if HAVE_WORKING_REGEX + std::wregex anAngleRegex(L"\\s*([-+]?[0-9]*\\.?[0-9]*)\\s*([-+])\\s*\\((.*)\\)$", + std::regex_constants::ECMAScript); +#endif + + double anAnglePrefix = 0.0; + static const wchar_t aSignPrefix[2] = { L'-', L'+' }; + int aSignInd = 1; + +#if HAVE_WORKING_REGEX + std::wsmatch aResult; + if (std::regex_search(anAngleText, aResult, anAngleRegex)) { +#else + // workaround to support old versions of GCC (less than 4.9) + std::wostringstream aResult[4]; + if (parseString(anAngleText, aResult)) { +#endif + anAnglePrefix = std::atof(Locale::Convert::toString(aResult[1].str()).c_str()); + aSignInd = aResult[2].str()[0] == aSignPrefix[0] ? 0 : 1; + anAngleText = aResult[3].str(); + } + + if (thePrevType != SketcherPrs_Tools::ANGLE_DIRECT) + aSignInd = 1 - aSignInd; + anAnglePrefix = angleForType(anAnglePrefix, thePrevType); + + if (theNewType != SketcherPrs_Tools::ANGLE_DIRECT) + aSignInd = 1 - aSignInd; + anAnglePrefix = angleForType(anAnglePrefix, theNewType); + + std::wostringstream aText; + bool isPrintSign = true; + if (fabs(anAnglePrefix) > tolerance) + aText << anAnglePrefix; + else + isPrintSign = aSignInd == 0; + if (isPrintSign) + aText << L" " << aSignPrefix[aSignInd] << L" ("; + aText << anAngleText << (isPrintSign ? L")" : L""); + theAngle->setText(aText.str()); + } + } +} - myFlyoutUpdate = true; - std::shared_ptr aFlyoutAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT())); - aFlyoutAttr->setValue(aFlyoutAttr->x() + theDeltaX, aFlyoutAttr->y() + theDeltaY); - myFlyoutUpdate = false; +void SketchPlugin_ConstraintAngle::updateAngleValue() +{ + AttributeIntegerPtr anAngleType = integer(TYPE_ID()); + AttributeIntegerPtr aPrevAngleType = integer(PREV_TYPE_ID()); + convertAngle(real(ANGLE_VALUE_ID()), aPrevAngleType->value(), anAngleType->value()); + aPrevAngleType->setValue(anAngleType->value()); } +static GeomPnt2dPtr lineBoundary(const FeaturePtr& theLine, const bool theReversed, + const GeomPnt2dPtr& thePointToAvoid) +{ + GeomPnt2dPtr aPoint = SketcherPrs_Tools::getPoint(theLine.get(), + theReversed ? SketchPlugin_Line::START_ID() : SketchPlugin_Line::END_ID()); + if (aPoint->distance(thePointToAvoid) < tolerance) { + // extremity is equal to the intersection point, + // thus recalculate it using another boundary point + aPoint = SketcherPrs_Tools::getPoint(theLine.get(), + theReversed ? SketchPlugin_Line::END_ID() : SketchPlugin_Line::START_ID()); + aPoint->setX(thePointToAvoid->x() * 2.0 - aPoint->x()); + aPoint->setY(thePointToAvoid->y() * 2.0 - aPoint->y()); + } + return aPoint; +} bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId) { @@ -260,57 +456,144 @@ bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId) std::shared_ptr aFlyOutAttr = std::dynamic_pointer_cast< GeomDataAPI_Point2D>(attribute(theAttributeId)); - if (fabs(aFlyOutAttr->x()) >= tolerance || fabs(aFlyOutAttr->y()) >= tolerance) - return false; DataPtr aData = data(); std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); - FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A()); - FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B()); + FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A()); + FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B()); if ((aLineA.get() == NULL) || (aLineB.get() == NULL)) return false; - // Start and end points of lines - std::shared_ptr aPointA1 = std::dynamic_pointer_cast( - aLineA->attribute(SketchPlugin_Line::START_ID())); - std::shared_ptr aPointA2 = std::dynamic_pointer_cast( - aLineA->attribute(SketchPlugin_Line::END_ID())); - - std::shared_ptr aStartA = aPointA1->pnt(); - std::shared_ptr aEndA = aPointA2->pnt(); - if (aStartA->distance(aEndA) < tolerance) + // Intersection of lines + std::shared_ptr anInter = intersect(aLineA, aLineB); + if (!anInter) return false; + bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value(); + bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value(); + + int anAngleType = integer(TYPE_ID())->value(); + + bool isSupplementary = anAngleType == (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY; + + // point on lines to compose an angle + GeomPnt2dPtr aPointA = lineBoundary(aLineA, isReversed1 ^ isSupplementary, anInter); + GeomPnt2dPtr aPointB = lineBoundary(aLineB, isReversed2, anInter); + myFlyoutUpdate = true; - double aX = (aStartA->x() + aEndA->x()) / 2.; - double aY = (aStartA->y() + aEndA->y()) / 2.; + if (aFlyOutAttr->isInitialized()) { + std::shared_ptr aFlyoutPoint = aFlyOutAttr->pnt()->xy(); + std::shared_ptr anInterXY = anInter->xy(); + std::shared_ptr aDirIF = aFlyoutPoint->decreased(anInterXY); + std::shared_ptr aDirIA = aPointA->xy()->decreased(anInterXY); + std::shared_ptr aDirIB = aPointB->xy()->decreased(anInterXY); + double aSign = aDirIA->cross(aDirIB); + aSign /= fabs(aSign); + if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) + aSign *= -1.0; + + double cross1 = aSign * aDirIA->cross(aDirIF); + if (cross1 < -tolerance) + boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(!isReversed2); + double cross2 = aSign * aDirIF->cross(aDirIB); + if (cross2 < -tolerance) + boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(!isReversed1); + + // the direction is reversed only once + if ((cross1 + tolerance) * (cross2 + tolerance) < 0.0) { + if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) { + convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_BACKWARD, + (int)SketcherPrs_Tools::ANGLE_DIRECT); + } + convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT, + (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY); + if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) { + convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT, + (int)SketcherPrs_Tools::ANGLE_BACKWARD); + } + } - aFlyOutAttr->setValue(aX, aY); + calculateAngle(); + } + else { + // default position of the presentation + double aX = (aPointA->x() + aPointB->x() + anInter->x()) / 3.; + double aY = (aPointA->y() + aPointB->y() + anInter->y()) / 3.; + aFlyOutAttr->setValue(aX, aY); + } myFlyoutUpdate = false; return true; } +void SketchPlugin_ConstraintAngle::updateVersion() +{ + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + + // Calculate angle value by the old algorithm and + // update the corresponding attribute to meet the new requirements. + FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_A()); + FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_B()); + + GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID()); + GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID()); + GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID()); + GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID()); + + std::shared_ptr anAng; + + if (boolean(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() && + boolean(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized()) { + bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value(); + bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value(); + + std::shared_ptr aLine1(new GeomAPI_Lin2d(aStartA, aEndA)); + std::shared_ptr aLine2(new GeomAPI_Lin2d(aStartB, aEndB)); + anAng.reset(new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2)); + } + else { + anAng.reset(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB)); + + bool isReversed1 = anAng->isReversed(0); + bool isReversed2 = anAng->isReversed(1); + + boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1); + boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2); + } + double anAngleValue = anAng->angleDegree(); + double aConstValue = real(ANGLE_VALUE_ID())->value(); + + AttributeIntegerPtr aType = integer(TYPE_ID()); + switch ((SketcherPrs_Tools::AngleType)aType->value()) { + case SketcherPrs_Tools::ANGLE_DIRECT: + if (anAngleValue < 0.0 && aConstValue > 180.0) + convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_BACKWARD, + SketcherPrs_Tools::ANGLE_DIRECT); + break; + case SketcherPrs_Tools::ANGLE_BACKWARD: + if (anAngleValue < 0.0 && aConstValue < 180.0) + convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_DIRECT, + SketcherPrs_Tools::ANGLE_BACKWARD); + break; + default: + break; + } + data()->blockSendAttributeUpdated(aWasBlocked, false); + integer(VERSION_ID())->setValue(THE_VERSION_1); +} + // =============== Auxiliary functions ================================== std::shared_ptr intersect(FeaturePtr theLine1, FeaturePtr theLine2) { // Start and end points of lines - std::shared_ptr aPointA1 = std::dynamic_pointer_cast( - theLine1->attribute(SketchPlugin_Line::START_ID())); - std::shared_ptr aPointA2 = std::dynamic_pointer_cast( - theLine1->attribute(SketchPlugin_Line::END_ID())); - - std::shared_ptr aPointB1 = std::dynamic_pointer_cast( - theLine2->attribute(SketchPlugin_Line::START_ID())); - std::shared_ptr aPointB2 = std::dynamic_pointer_cast( - theLine2->attribute(SketchPlugin_Line::END_ID())); - - std::shared_ptr aStartA = aPointA1->pnt(); - std::shared_ptr aEndA = aPointA2->pnt(); - std::shared_ptr aStartB = aPointB1->pnt(); - std::shared_ptr aEndB = aPointB2->pnt(); + const std::string& aLineStartAttr = SketchPlugin_Line::START_ID(); + const std::string& aLineEndAttr = SketchPlugin_Line::END_ID(); + GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(theLine1.get(), aLineStartAttr); + GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(theLine1.get(), aLineEndAttr); + GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(theLine2.get(), aLineStartAttr); + GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(theLine2.get(), aLineEndAttr); if (aStartA->distance(aEndA) < tolerance || aStartB->distance(aEndB) < tolerance) std::shared_ptr();