+ double anAngle = angleForType(theAngle, integer(TYPE_ID())->value());
+ if (isReversed1 != isReversed2)
+ anAngle = 180.0 - anAngle;
+ return anAngle;
+}
+
+#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++];
+ }
+
+ // 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
+
+// Convert angle value or a text expression from one angle type to another
+static void convertAngle(AttributeDoublePtr theAngle,
+ const int thePrevType, const int theNewType)
+{
+ 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());
+ }
+ }
+}
+
+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;