X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchPlugin%2FSketchPlugin_Arc.cpp;h=c72230a16b5d0c438065061bbc6a2025a27121f7;hb=fc72d43b677baa05ae7fd317346fd8b723b799ed;hp=be7ad7dc72845ce56f0df6a86574181a88e2c311;hpb=5b6031b015602aa07f5a6fc668c13ac3faf7a8a9;p=modules%2Fshaper.git diff --git a/src/SketchPlugin/SketchPlugin_Arc.cpp b/src/SketchPlugin/SketchPlugin_Arc.cpp index be7ad7dc7..c72230a16 100644 --- a/src/SketchPlugin/SketchPlugin_Arc.cpp +++ b/src/SketchPlugin/SketchPlugin_Arc.cpp @@ -1,8 +1,21 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> - -// File: SketchPlugin_Arc.cpp -// Created: 26 Apr 2014 -// Author: Artem ZHIDKOV +// Copyright (C) 2014-2023 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 +// #include "SketchPlugin_Arc.h" #include "SketchPlugin_Sketch.h" @@ -37,302 +50,114 @@ #include #include // for sqrt on Linux -#include - -const double tolerance = 1e-7; -const double paramTolerance = 1.e-4; -const double PI = 3.141592653589793238463; - -namespace { - static const std::string& POINT_ID(int theIndex) - { - switch (theIndex) { - case 1: return SketchPlugin_Arc::START_ID(); - case 2: return SketchPlugin_Arc::END_ID(); - case 3: return SketchPlugin_Arc::PASSED_POINT_ID(); - } - - static const std::string DUMMY; - return DUMMY; - } -} +#include +static const double tolerance = 1e-7; +static const double paramTolerance = 1.e-4; +static const double PI = 3.141592653589793238463; SketchPlugin_Arc::SketchPlugin_Arc() - : SketchPlugin_SketchEntity() +: SketchPlugin_SketchEntity() { - myStartUpdate = false; - myEndUpdate = false; - // default values - myXEndBefore = 0; - myYEndBefore = 0; - - myParamBefore = PI * 2.0; + myParamBefore = 0.0; } void SketchPlugin_Arc::initDerivedClassAttributes() { data()->addAttribute(CENTER_ID(), GeomDataAPI_Point2D::typeId()); data()->addAttribute(START_ID(), GeomDataAPI_Point2D::typeId()); - std::shared_ptr anEndAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->addAttribute(END_ID(), GeomDataAPI_Point2D::typeId())); + data()->addAttribute(END_ID(), GeomDataAPI_Point2D::typeId()); + data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId()); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID()); - AttributeBooleanPtr isInversed = std::dynamic_pointer_cast( - data()->addAttribute(INVERSED_ID(), ModelAPI_AttributeBoolean::typeId())); - - // get the initial values - if (anEndAttr->isInitialized()) { - myXEndBefore = anEndAttr->x(); - myYEndBefore = anEndAttr->y(); - } - - AttributeStringPtr anArcType = std::dynamic_pointer_cast( - data()->addAttribute(ARC_TYPE(), ModelAPI_AttributeString::typeId())); + AttributeBooleanPtr isReversed = std::dynamic_pointer_cast( + data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId())); - data()->addAttribute(PASSED_POINT_ID(), GeomDataAPI_Point2D::typeId()); - data()->addAttribute(TANGENT_POINT_ID(), ModelAPI_AttributeRefAttr::typeId()); data()->addAttribute(RADIUS_ID(), ModelAPI_AttributeDouble::typeId()); data()->addAttribute(ANGLE_ID(), ModelAPI_AttributeDouble::typeId()); // set after all to avoid in attributeChanged reference to not existing attributes - if (!isInversed->isInitialized()) - isInversed->setValue(false); - anArcType->setValue(ARC_TYPE_CENTER_START_END()); + if (!isReversed->isInitialized()) { + isReversed->setValue(false); + } } void SketchPlugin_Arc::execute() { SketchPlugin_Sketch* aSketch = sketch(); - // result for the arc is set only when all obligatory attributes are initialized, - // otherwise AIS object is used to visualize the arc's preview - if (aSketch && isFeatureValid()) { - bool hasResult = lastResult().get() != NULL; - - // compute a circle point in 3D view - std::shared_ptr aCenterAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(CENTER_ID())); - // compute the arc start point - std::shared_ptr aStartAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(START_ID())); - - std::shared_ptr aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y())); - // make a visible point - std::shared_ptr aCenterPointShape = GeomAlgoAPI_PointBuilder::vertex(aCenter); - std::shared_ptr aConstr1 = document()->createConstruction( - data(), 0); - aConstr1->setShape(aCenterPointShape); - aConstr1->setIsInHistory(false); - setResult(aConstr1, 0); - - // make a visible circle - std::shared_ptr aNDir = std::dynamic_pointer_cast( - aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID())); - std::shared_ptr aNormal = aNDir->dir(); - std::shared_ptr aStartPoint(aSketch->to3D(aStartAttr->x(), aStartAttr->y())); - - // compute and change the arc end point - std::shared_ptr anEndAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(END_ID())); + if(!aSketch) { + return; + } + + std::shared_ptr aCenterAttr = + std::dynamic_pointer_cast(data()->attribute(CENTER_ID())); + std::shared_ptr aStartAttr = + std::dynamic_pointer_cast(data()->attribute(START_ID())); + std::shared_ptr anEndAttr = + std::dynamic_pointer_cast(data()->attribute(END_ID())); + if(!aCenterAttr->isInitialized() || !aStartAttr->isInitialized() || !anEndAttr->isInitialized()) { + return; + } + + // Make a visible point. + SketchPlugin_Sketch::createPoint2DResult(this, sketch(), CENTER_ID(), 0); + + // Make a visible arc. + std::shared_ptr aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y())); + std::shared_ptr aStart(aSketch->to3D(aStartAttr->x(), aStartAttr->y())); + std::shared_ptr anEnd(aSketch->to3D(anEndAttr->x(), anEndAttr->y())); + std::shared_ptr aNDir = std::dynamic_pointer_cast( + aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID())); + std::shared_ptr aNormal(new GeomAPI_Dir(aNDir->x(), aNDir->y(), aNDir->z())); + + if (myParamBefore == 0) { // parameter has not been calculate yet std::shared_ptr aCircleForArc( new GeomAPI_Circ2d(aCenterAttr->pnt(), aStartAttr->pnt())); - std::shared_ptr aProjection = aCircleForArc->project(anEndAttr->pnt()); - if (aProjection && anEndAttr->pnt()->distance(aProjection) > tolerance) - anEndAttr->setValue(aProjection); - std::shared_ptr aEndPoint(aSketch->to3D(anEndAttr->x(), anEndAttr->y())); - AttributeBooleanPtr isInversed = - std::dynamic_pointer_cast(attribute(INVERSED_ID())); - - // compute end parameter aCircleForArc->parameter(anEndAttr->pnt(), paramTolerance, myParamBefore); + } - std::shared_ptr aCircleShape; - if(!isInversed->value()) { - aCircleShape = - GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aStartPoint, aEndPoint, aNormal); - } else { - aCircleShape = - GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aEndPoint, aStartPoint, aNormal); - } + bool isReversed = boolean(REVERSED_ID())->value(); - if (aCircleShape) { - std::shared_ptr aConstr2 = document()->createConstruction( - data(), 1); - aConstr2->setShape(aCircleShape); - aConstr2->setIsInHistory(false); - setResult(aConstr2, 1); - } + GeomEdgePtr anArcShape; + if (fabs(myParamBefore - 2.0 * PI) < paramTolerance) { + anArcShape = GeomAlgoAPI_EdgeBuilder::lineCircle(aCenter, aNormal, aStart->distance(aCenter)); + myParamBefore = 0; + } else { + anArcShape = isReversed ? + GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, anEnd, aStart, aNormal) + : GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aStart, anEnd, aNormal); } -} -AISObjectPtr SketchPlugin_Arc::getAISObject(AISObjectPtr thePrevious) -{ - SketchPlugin_Sketch* aSketch = sketch(); - if (aSketch) { - // if the feature is valid, the execute() method should be performed, AIS object is empty - if (!isFeatureValid()) { - // compute a circle point in 3D view - std::shared_ptr aCenterAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(CENTER_ID())); - - std::list > aShapes; - if (aCenterAttr->isInitialized()) { - std::shared_ptr aCenter(aSketch->to3D(aCenterAttr->x(), aCenterAttr->y())); - - std::shared_ptr aStartAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID())); - std::shared_ptr aEndAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::END_ID())); - AttributeStringPtr aTypeAttr = std::dynamic_pointer_cast( - data()->attribute(ARC_TYPE())); - - if (aStartAttr->isInitialized()) { - // make a visible circle - std::shared_ptr aNDir = std::dynamic_pointer_cast( - aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID())); - bool aHasPlane = aNDir && !(aNDir->x() == 0 && aNDir->y() == 0 && aNDir->z() == 0); - if (aHasPlane) { - std::shared_ptr aNormal = aNDir->dir(); - std::shared_ptr - aStartPoint(aSketch->to3D(aStartAttr->x(), aStartAttr->y())); - std::shared_ptr aEndPoint = aStartPoint; - if (aTypeAttr && aTypeAttr->isInitialized() && - aTypeAttr->value() == ARC_TYPE_THREE_POINTS()) { - if (aEndAttr->isInitialized() && // - aEndAttr->pnt()->distance(aStartAttr->pnt()) > tolerance) { - aEndPoint = aSketch->to3D(aEndAttr->x(), aEndAttr->y()); - std::shared_ptr aPassedAttr = - std::dynamic_pointer_cast( - data()->attribute(PASSED_POINT_ID())); - if (!aPassedAttr->isInitialized()) { - // calculate the appropriate center for the presentation - // check that center is bad for the current start and end and must be recomputed - std::shared_ptr aCircleForArc(new GeomAPI_Circ2d( - aCenterAttr->pnt(), aStartAttr->pnt())); - std::shared_ptr aProjection = - aCircleForArc->project(aEndAttr->pnt()); - if (!aProjection.get() || aEndAttr->pnt()->distance(aProjection) > tolerance) { - std::shared_ptr aDir = - aEndAttr->pnt()->xy()->decreased(aStartAttr->pnt()->xy())->multiplied(0.5); - double x = aDir->x(); - double y = aDir->y(); - aDir->setX(x - y); - aDir->setY(y + x); - std::shared_ptr aCenterXY = aStartAttr->pnt()->xy()->added(aDir); - aCenter = aSketch->to3D(aCenterXY->x(), aCenterXY->y()); - } - } - } else { // issue #1695: don't display circle if initialized only start point - return AISObjectPtr(); - } - } - AttributeBooleanPtr isInversed = - std::dynamic_pointer_cast(attribute(INVERSED_ID())); - - std::shared_ptr aCircleShape = - (isInversed->isInitialized() && isInversed->value()) ? - GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aEndPoint, aStartPoint, aNormal) : - GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenter, aStartPoint, aEndPoint, aNormal); - - if (aCircleShape) - aShapes.push_back(aCircleShape); - } - } - // make a visible point - std::shared_ptr aCenterPointShape = - GeomAlgoAPI_PointBuilder::vertex(aCenter); - aShapes.push_back(aCenterPointShape); - } - if (!aShapes.empty()) { - std::shared_ptr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes); - AISObjectPtr anAIS = thePrevious; - if (!anAIS) - anAIS = AISObjectPtr(new GeomAPI_AISObject); - anAIS->createShape(aCompound); - double aDeflection = Config_PropManager::real("Visualization", "construction_deflection", - ModelAPI_ResultConstruction::DEFAULT_DEFLECTION()); - anAIS->setDeflection(aDeflection); - anAIS->setWidth(3); - return anAIS; + // calculate tolerances for start and end points of the arc and set them to the result shape + // (this is done to fix gaps which appear because of inaccurate computation of arcs in PlaneGCS, + // which leads to difference in SketchPlugin_Arc attributes and boundary points of result shape) + if (anArcShape) { + for (int ind = 0; ind < 2; ++ind) { + bool isFirst = ind == 0; + GeomPointPtr anArcBndPoint = isFirst == isReversed ? anEnd : aStart; + GeomPointPtr aShapePoint = isFirst ? anArcShape->firstPoint() : anArcShape->lastPoint(); + double aDistance = anArcBndPoint->distance(aShapePoint); + // avoid setting too high tolerance because it may be caused by incomplete update of an arc + if (aDistance > tolerance && aDistance < 100. * tolerance) { + if (isFirst) + anArcShape->setFirstPointTolerance(aDistance); + else + anArcShape->setLastPointTolerance(aDistance); } } } - return AISObjectPtr(); -} -void SketchPlugin_Arc::move(double theDeltaX, double theDeltaY) -{ - std::shared_ptr aData = data(); - if (!aData->isValid()) - return; - - aData->blockSendAttributeUpdated(true); - - myStartUpdate = true; - myEndUpdate = true; - std::shared_ptr aPoint2 = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Arc::START_ID())); - if (aPoint2->isInitialized()) - aPoint2->move(theDeltaX, theDeltaY); - - std::shared_ptr aPoint3 = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Arc::END_ID())); - if (aPoint3->isInitialized()) - aPoint3->move(theDeltaX, theDeltaY); - myStartUpdate = false; - myEndUpdate = false; - - std::shared_ptr aPoint1 = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Arc::CENTER_ID())); - if (aPoint1->isInitialized()) - aPoint1->move(theDeltaX, theDeltaY); - - std::shared_ptr aPassedPoint = - std::dynamic_pointer_cast(aData->attribute(PASSED_POINT_ID())); - if (aPassedPoint->isInitialized()) - aPassedPoint->move(theDeltaX, theDeltaY); - aData->blockSendAttributeUpdated(false); -} - -bool SketchPlugin_Arc::isFixed() { - return data()->selection(EXTERNAL_ID())->context().get() != NULL; + std::shared_ptr aResult = document()->createConstruction(data(), 1); + aResult->setShape(anArcShape); + aResult->setIsInHistory(false); + setResult(aResult, 1); } -bool SketchPlugin_Arc::isFeatureValid() +bool SketchPlugin_Arc::isFixed() { - AttributeStringPtr anArcTypeAttr = - std::dynamic_pointer_cast(data()->attribute(ARC_TYPE())); - if (!anArcTypeAttr) - return false; - std::string anArcType = anArcTypeAttr->value(); - - std::shared_ptr aCenterAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::CENTER_ID())); - std::shared_ptr aStartAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::START_ID())); - std::shared_ptr anEndAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(SketchPlugin_Arc::END_ID())); - std::shared_ptr aPassedAttr = std::dynamic_pointer_cast< - GeomDataAPI_Point2D>(data()->attribute(PASSED_POINT_ID())); - - bool isValid = false; - if (anArcType == ARC_TYPE_THREE_POINTS()) - isValid = aStartAttr->isInitialized() && - anEndAttr->isInitialized() && aPassedAttr->isInitialized(); - else - isValid = aCenterAttr->isInitialized() && - aStartAttr->isInitialized() && anEndAttr->isInitialized(); - - return isValid; -} - -static inline void adjustPeriod(double& theParam) -{ - static const double PERIOD = 2.0 * PI; - while (theParam < 0.0) theParam += PERIOD; - while (theParam >= PERIOD) theParam -= PERIOD; + return data()->selection(EXTERNAL_ID())->context().get() != NULL; } void SketchPlugin_Arc::attributeChanged(const std::string& theID) @@ -343,96 +168,68 @@ void SketchPlugin_Arc::attributeChanged(const std::string& theID) GeomDataAPI_Point2D>(data()->attribute(START_ID())); std::shared_ptr anEndAttr = std::dynamic_pointer_cast< GeomDataAPI_Point2D>(data()->attribute(END_ID())); - // the second condition for unability to move external segments anywhere - if (theID == EXTERNAL_ID() || isFixed()) { + + // The second condition for unability to move external segments anywhere. + if(theID == EXTERNAL_ID() || isFixed()) { std::shared_ptr aSelection = data()->selection(EXTERNAL_ID())->value(); - if (!aSelection) { + if(!aSelection) { // empty shape in selection shows that the shape is equal to context ResultPtr anExtRes = selection(EXTERNAL_ID())->context(); - if (anExtRes) + if(anExtRes) { aSelection = anExtRes->shape(); + } } // update arguments due to the selection value - if (aSelection && !aSelection->isNull() && aSelection->isEdge()) { + if(aSelection && !aSelection->isNull() && aSelection->isEdge()) { std::shared_ptr anEdge( new GeomAPI_Edge(aSelection)); std::shared_ptr aCirc = anEdge->circle(); - if (aCirc.get()) { + if(aCirc.get()) { + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + aCenterAttr->setValue(sketch()->to2D(aCirc->center())); aStartAttr->setValue(sketch()->to2D(anEdge->firstPoint())); anEndAttr->setValue(sketch()->to2D(anEdge->lastPoint())); - aCenterAttr->setValue(sketch()->to2D(aCirc->center())); - - data()->real(RADIUS_ID())->setValue(aCirc->radius()); - double aStartAngle, aEndAngle; - anEdge->getRange(aStartAngle, aEndAngle); - data()->real(ANGLE_ID())->setValue(aEndAngle - aStartAngle); - myParamBefore = aEndAngle; - adjustPeriod(myParamBefore); - } - } - return; - } - AttributeStringPtr aTypeAttr = - std::dynamic_pointer_cast(attribute(ARC_TYPE())); - - // this is before others since here end attribute may be changed, but with the special behavior - if (aTypeAttr->value() == ARC_TYPE_TANGENT() && - (theID == TANGENT_POINT_ID() || theID == END_ID())) { - SketchPlugin_Sketch* aSketch = sketch(); - AttributeRefAttrPtr aTangPtAttr = std::dynamic_pointer_cast( - data()->attribute(TANGENT_POINT_ID())); - - if (aTangPtAttr->isInitialized() && anEndAttr->isInitialized()) { - // compute orthogonal direction - std::shared_ptr anOrthoDir; - std::shared_ptr aTangentPoint = - std::dynamic_pointer_cast(aTangPtAttr->attr()); - std::shared_ptr aTangPnt2d = aTangentPoint->pnt(); - if (aTangPnt2d->isEqual(anEndAttr->pnt())) - return; - FeaturePtr aTangFeature = ModelAPI_Feature::feature(aTangentPoint->owner()); - std::shared_ptr aTangEdge = std::dynamic_pointer_cast( - aTangFeature->lastResult()->shape()); - if (aTangEdge->isLine()) { - std::shared_ptr aDir = aTangEdge->line()->direction(); - std::shared_ptr aPnt(new GeomAPI_Pnt(aDir->x(), aDir->y(), aDir->z())); - std::shared_ptr aPnt2d = aSketch->to2D(aPnt); - anOrthoDir = std::shared_ptr(new GeomAPI_Dir2d(-aPnt2d->y(), aPnt2d->x())); - } - else if (aTangEdge->isArc()) { - std::shared_ptr aCenter = aTangEdge->circle()->center(); - std::shared_ptr aCenter2d = aSketch->to2D(aCenter); - anOrthoDir = std::shared_ptr( - new GeomAPI_Dir2d(aTangPnt2d->xy()->decreased(aCenter2d->xy()))); - } - - // compute parameters of the middle perpendicular - std::shared_ptr aEndPntCoord = anEndAttr->pnt()->xy(); - std::shared_ptr aTempDir = aEndPntCoord->decreased(aTangPnt2d->xy()); - std::shared_ptr aMidDir(new GeomAPI_Dir2d(-aTempDir->y(), aTempDir->x())); - std::shared_ptr aMidPnt( - new GeomAPI_Pnt2d(aEndPntCoord->added(aTangPnt2d->xy())->multiplied(0.5))); - - // compute center of arc by calculating intersection of - // orthogonal line and middle perpendicular - std::shared_ptr anOrthoLine(new GeomAPI_Lin2d(aTangPnt2d, anOrthoDir)); - std::shared_ptr aMiddleLine(new GeomAPI_Lin2d(aMidPnt, aMidDir)); - std::shared_ptr aCenter = anOrthoLine->intersect(aMiddleLine); - if (aCenter) { - data()->blockSendAttributeUpdated(true); - aCenterAttr->setValue(aCenter); - aStartAttr->setValue(aTangPnt2d); - data()->blockSendAttributeUpdated(false); + data()->blockSendAttributeUpdated(aWasBlocked, false); + + std::shared_ptr aCircle2d = + std::shared_ptr(new GeomAPI_Circ2d(aCenterAttr->pnt(), + aStartAttr->pnt())); + + double anEndParam = 0.0; + aCircle2d->parameter(anEndAttr->pnt(), paramTolerance, anEndParam); + myParamBefore = anEndParam; + + double aMidParam = anEndParam / 2.0; + std::shared_ptr aMidPnt2d; + aCircle2d->D0(aMidParam, aMidPnt2d); + std::shared_ptr aMinPnt = sketch()->to3D(aMidPnt2d->x(), aMidPnt2d->y()); + double aStartParam = 0.0; + aCirc->parameter(anEdge->firstPoint(), paramTolerance, aStartParam); + aCirc->parameter(aMinPnt, paramTolerance, aMidParam); + aCirc->parameter(anEdge->lastPoint(), paramTolerance, anEndParam); + + // adjust period + anEndParam -= aStartParam; + aMidParam -= aStartParam; + if (anEndParam < 0.0) + anEndParam += 2.0 * PI; + if (aMidParam < 0.0) + aMidParam += 2.0 * PI; + + aWasBlocked = data()->blockSendAttributeUpdated(true); + if(aMidParam < anEndParam) { + setReversed(false); + } else { + setReversed(true); + } + data()->blockSendAttributeUpdated(aWasBlocked, false); } - - tangencyArcConstraints(); } - return; - } - - // if changed the base attributes, update all other (is necessary) without recursion - if (theID == CENTER_ID() || theID == START_ID() || theID == END_ID() || theID == ARC_TYPE()) { - if (!isFeatureValid()) + } else if(theID == CENTER_ID() || theID == START_ID() || theID == END_ID()) { + if(!aCenterAttr->isInitialized() + || !aStartAttr->isInitialized() + || !anEndAttr->isInitialized()) { return; + } std::shared_ptr aCenter = aCenterAttr->pnt(); std::shared_ptr aStart = aStartAttr->pnt(); std::shared_ptr anEnd = anEndAttr->pnt(); @@ -441,326 +238,60 @@ void SketchPlugin_Arc::attributeChanged(const std::string& theID) return; std::shared_ptr aCircleForArc(new GeomAPI_Circ2d(aCenter, aStart)); - data()->blockSendAttributeUpdated(true); - // The Arc end point is projected - // on the circle formed by center and start points + // Do not recalculate REVERSED flag if the arc is not consistent std::shared_ptr aProjection = aCircleForArc->project(anEnd); - if (aProjection && anEnd->distance(aProjection) > tolerance) { - anEndAttr->setValue(aProjection); - anEnd = aProjection; - } - // update all other attributes due to the base attributes values - if (aTypeAttr->value() == ARC_TYPE_THREE_POINTS()) { - // update passed point due to start, end and center - if (aCenter->distance(aStart) > tolerance && aCenter->distance(anEnd) > tolerance) { - // project passed point t othe circle - std::shared_ptr aPassedAttr = - std::dynamic_pointer_cast(data()->attribute(PASSED_POINT_ID())); - if (aPassedAttr->isInitialized()) { - std::shared_ptr aProjection = aCircleForArc->project(aPassedAttr->pnt()); - if (aProjection && aPassedAttr->pnt()->distance(aProjection) > tolerance) { - aPassedAttr->setValue(aProjection); + if (aProjection && anEnd->distance(aProjection) <= tolerance) { + double aParameterNew = 0.0; + if(aCircleForArc->parameter(anEnd, paramTolerance, aParameterNew)) { + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + if(myParamBefore <= PI / 2.0 && aParameterNew >= PI * 1.5) { + if(!boolean(REVERSED_ID())->value()) { + boolean(REVERSED_ID())->setValue(true); } - } else { // initialize it by some middle - value - std::shared_ptr aStartDir(new GeomAPI_Dir2d( - aStart->xy()->decreased(aCenter->xy()))); - std::shared_ptr aEndDir(new GeomAPI_Dir2d( - anEnd->xy()->decreased(aCenter->xy()))); - std::shared_ptr aMidDirXY = aStartDir->xy()->added(aEndDir->xy()); - if (aMidDirXY->dot(aMidDirXY) < tolerance * tolerance) { - // start and end directions are opposite, so middle direction will be orthogonal - aMidDirXY->setX(-aStartDir->y()); - aMidDirXY->setY(aStartDir->x()); + } else if(myParamBefore >= PI * 1.5 && aParameterNew <= PI / 2.0) { + if(boolean(REVERSED_ID())->value()) { + boolean(REVERSED_ID())->setValue(false); } - std::shared_ptr aMidDir(new GeomAPI_Dir2d(aMidDirXY)); - if ((aStartDir->cross(aMidDir) > 0) ^ !isReversed()) - aMidDir->reverse(); - std::shared_ptr aPassedPnt = - aCenter->xy()->added(aMidDir->xy()->multiplied(aCenter->distance(aStart))); - std::dynamic_pointer_cast(data()->attribute(PASSED_POINT_ID()))-> - setValue(aPassedPnt->x(), aPassedPnt->y()); } + data()->blockSendAttributeUpdated(aWasBlocked, false); } + if (fabs(aParameterNew) < paramTolerance || + fabs(aParameterNew - 2.0 * PI) < paramTolerance) + aParameterNew = 2.0 * PI; + myParamBefore = aParameterNew; } - // update radius and angle - AttributeDoublePtr aRadiusAttr = std::dynamic_pointer_cast( - data()->attribute(RADIUS_ID())); - aRadiusAttr->setValue(aRadius); - AttributeDoublePtr anAngleAttr = std::dynamic_pointer_cast( - data()->attribute(ANGLE_ID())); - std::shared_ptr aCircle(new GeomAPI_Circ2d(aCenter, aStart)); - double aStartParam, aEndParam; - aCircle->parameter(aStart, paramTolerance, aStartParam); - aCircle->parameter(anEnd, paramTolerance, aEndParam); - adjustPeriod(aStartParam); - adjustPeriod(aEndParam); - // use the passed point for the angle calculation - if (aTypeAttr->value() == ARC_TYPE_THREE_POINTS()) { - std::shared_ptr aPassedAttr = - std::dynamic_pointer_cast(data()->attribute(PASSED_POINT_ID())); - double aPassedParam; - aCircle->parameter(aPassedAttr->pnt(), paramTolerance, aPassedParam); - adjustPeriod(aPassedParam); - double aNewAngle = aPassedParam >= aStartParam && aPassedParam <= aEndParam ? - ((aEndParam - aStartParam) * 180.0 / PI) : - ((aEndParam - aStartParam - 2.0 * PI) * 180.0 / PI); - if (!anAngleAttr->isInitialized() || fabs(aNewAngle - anAngleAttr->value()) > tolerance) - anAngleAttr->setValue(aNewAngle); - } else { - double aNewAngle = (aEndParam - aStartParam) * 180.0 / PI; - if (!anAngleAttr->isInitialized() || fabs(aNewAngle - anAngleAttr->value()) > tolerance) - anAngleAttr->setValue(aNewAngle); - } - - // calculate arc aperture and change the Inversed flag if needed - AttributeBooleanPtr isInversed = - std::dynamic_pointer_cast(attribute(INVERSED_ID())); - double aParameterNew = aEndParam - aStartParam; - if (((0 <= myParamBefore && myParamBefore <= PI / 2.0) || myParamBefore == PI * 2.0) && - PI * 1.5 <= aParameterNew && aParameterNew <= PI * 2.0) - isInversed->setValue(true); - else if (PI * 1.5 <= myParamBefore && myParamBefore <= PI * 2.0 && - 0 <= aParameterNew && aParameterNew <= PI / 2.0) - isInversed->setValue(false); - myParamBefore = aParameterNew; - - // do not need to inform that other parameters were changed in this basis mode: these arguments - // change is enough - data()->blockSendAttributeUpdated(false, false); - return; } - if (theID == PASSED_POINT_ID()) { - data()->blockSendAttributeUpdated(true); - - std::shared_ptr aPoints[3]; - int aNbInitialized = 0; - for (int i = 1; i <= 3; ++i) { - std::shared_ptr aCurPnt = - std::dynamic_pointer_cast(attribute(POINT_ID(i))); - if (aCurPnt->isInitialized()) - aPoints[aNbInitialized++] = aCurPnt->pnt(); - } - - if (aNbInitialized == 3) { - std::shared_ptr aCircle( - new GeomAPI_Circ2d(aPoints[0], aPoints[1], aPoints[2])); - - std::shared_ptr aCenter = aCircle->center(); - if (aCenter) { - aCenterAttr->setValue(aCenter); + double aRadius = 0; + double anAngle = 0; + if(aCenterAttr->isInitialized() && aStartAttr->isInitialized()) { + aRadius = aCenterAttr->pnt()->distance(aStartAttr->pnt()); + if(anEndAttr->isInitialized()) { + if(aStartAttr->pnt()->isEqual(anEndAttr->pnt())) { + anAngle = 360; + } else { + GeomAPI_Circ2d aCircleForArc(aCenterAttr->pnt(), aStartAttr->pnt()); + double aStartParam, anEndParam; + aCircleForArc.parameter(aStartAttr->pnt(), paramTolerance, aStartParam); + aCircleForArc.parameter(anEndAttr->pnt(), paramTolerance, anEndParam); + anAngle = (anEndParam - aStartParam) / PI * 180.0; + if(isReversed()) anAngle = 360.0 - anAngle; } } - data()->blockSendAttributeUpdated(false); - return; } - if (theID == RADIUS_ID()) { - if (!aStartAttr->isInitialized() || !anEndAttr->isInitialized() || - !aCenterAttr->isInitialized()) - return; - // move center and passed point - std::shared_ptr aStart = aStartAttr->pnt(); - std::shared_ptr anEnd = anEndAttr->pnt(); - std::shared_ptr aCenter = aCenterAttr->pnt(); - if (aStart->distance(aCenter) < tolerance || anEnd->distance(aCenter) < tolerance) - return; - AttributeDoublePtr aRadiusAttr = std::dynamic_pointer_cast( - data()->attribute(RADIUS_ID())); - double aRadius = aRadiusAttr->value(); - - data()->blockSendAttributeUpdated(true); - std::shared_ptr - aStartDir(new GeomAPI_Dir2d(aStart->xy()->decreased(aCenter->xy()))); - std::shared_ptr - aNewStart = aStartDir->xy()->multiplied(aRadius)->added(aCenter->xy()); - aStartAttr->setValue(aNewStart->x(), aNewStart->y()); - std::shared_ptr - anEndDir(new GeomAPI_Dir2d(anEnd->xy()->decreased(aCenter->xy()))); - std::shared_ptr - aNewEnd = anEndDir->xy()->multiplied(aRadius)->added(aCenter->xy()); - anEndAttr->setValue(aNewEnd->x(), aNewEnd->y()); - data()->blockSendAttributeUpdated(false); - return; - } - if (theID == ANGLE_ID()) { - if (!aStartAttr->isInitialized() || !aCenterAttr->isInitialized()) - return; - AttributeDoublePtr anAngleAttr = std::dynamic_pointer_cast( - data()->attribute(ANGLE_ID())); - data()->blockSendAttributeUpdated(true); - // move end point and passed point - std::shared_ptr aCenter = aCenterAttr->pnt()->xy(); - double anAngle = anAngleAttr->value() * PI / 180.0; - double sinA = sin(anAngle); - double cosA = cos(anAngle); - std::shared_ptr aStartDir = aStartAttr->pnt()->xy()->decreased(aCenter); - std::shared_ptr aDir(new GeomAPI_XY( - aStartDir->x() * cosA - aStartDir->y() * sinA, - aStartDir->x() * sinA + aStartDir->y() * cosA)); - anEndAttr->setValue(aCenter->x() + aDir->x(), aCenter->y() + aDir->y()); - data()->blockSendAttributeUpdated(false); - return; - } + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + real(RADIUS_ID())->setValue(aRadius); + real(ANGLE_ID())->setValue(anAngle); + data()->blockSendAttributeUpdated(aWasBlocked, false); } void SketchPlugin_Arc::setReversed(bool isReversed) { - std::dynamic_pointer_cast( - attribute(INVERSED_ID()))->setValue(isReversed); - myParamBefore = 0.0; + boolean(REVERSED_ID())->setValue(isReversed); } bool SketchPlugin_Arc::isReversed() { - return std::dynamic_pointer_cast(attribute(INVERSED_ID()))->value(); -} - -void SketchPlugin_Arc::tangencyArcConstraints() -{ - if (!lastResult()) - return; - - std::shared_ptr aStartAttr = - std::dynamic_pointer_cast(attribute(START_ID())); - AttributeRefAttrPtr aTangPtAttr = std::dynamic_pointer_cast( - attribute(TANGENT_POINT_ID())); - if (!aTangPtAttr->attr()) - return; - - FeaturePtr aFeature = ModelAPI_Feature::feature(aStartAttr->owner()); - ObjectPtr aThisArc = aFeature->lastResult(); - aFeature = ModelAPI_Feature::feature(aTangPtAttr->attr()->owner()); - ObjectPtr aTangFeature = aFeature->lastResult(); - - // trying to find constraints to fix the tangency of the arc - std::set aCoincidence; - std::set aTangency; - - AttributeRefAttrPtr aRefAttrA, aRefAttrB; - std::set aRefs = data()->refsToMe(); - const std::set& aRefsToResult = lastResult()->data()->refsToMe(); - aRefs.insert(aRefsToResult.begin(), aRefsToResult.end()); - std::set::const_iterator aRefIt = aRefs.begin(); - for (; aRefIt != aRefs.end(); ++aRefIt) { - FeaturePtr aConstrFeature = ModelAPI_Feature::feature((*aRefIt)->owner()); - if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { - aRefAttrA = std::dynamic_pointer_cast( - aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_B())); - if ((aRefAttrA && aRefAttrA->attr() == aStartAttr) || - (aRefAttrB && aRefAttrB->attr() == aStartAttr)) - aCoincidence.insert(aConstrFeature); - } - else if (aConstrFeature->getKind() == SketchPlugin_ConstraintTangent::ID()) { - aRefAttrA = std::dynamic_pointer_cast( - aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - aConstrFeature->attribute(SketchPlugin_Constraint::ENTITY_B())); - if ((aRefAttrA && aRefAttrA->object() == aThisArc) || - (aRefAttrB && aRefAttrB->object() == aThisArc)) - aTangency.insert(aConstrFeature); - } - } - // search applicable pair of constraints - bool isFound = false; - FeaturePtr aPrevCoincidence, aPrevTangency; - std::set::const_iterator aCIt, aTIt; - for (aCIt = aCoincidence.begin(); aCIt != aCoincidence.end() && !isFound; ++aCIt) { - aRefAttrA = std::dynamic_pointer_cast( - (*aCIt)->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - (*aCIt)->attribute(SketchPlugin_Constraint::ENTITY_B())); - AttributePtr anOtherPoint = - aRefAttrA->attr() == aStartAttr ? aRefAttrB->attr() : aRefAttrA->attr(); - for (aTIt = aTangency.begin(); aTIt != aTangency.end() && !isFound; ++aTIt) { - aRefAttrA = std::dynamic_pointer_cast( - (*aTIt)->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - (*aTIt)->attribute(SketchPlugin_Constraint::ENTITY_B())); - ObjectPtr anOtherObject = aRefAttrA->object() == aThisArc ? - aRefAttrB->object() : aRefAttrA->object(); - FeaturePtr anOtherFeature = ModelAPI_Feature::feature(anOtherObject); - if (anOtherPoint->owner() == anOtherFeature) { - isFound = true; - aPrevCoincidence = *aCIt; - aPrevTangency = *aTIt; - } - } - } - - if (isFound) { - // update previous constraints - aRefAttrA = std::dynamic_pointer_cast( - aPrevCoincidence->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - aPrevCoincidence->attribute(SketchPlugin_Constraint::ENTITY_B())); - if (aRefAttrA->attr() == aStartAttr) - aRefAttrB->setAttr(aTangPtAttr->attr()); - else - aRefAttrA->setAttr(aTangPtAttr->attr()); - - aRefAttrA = std::dynamic_pointer_cast( - aPrevTangency->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - aPrevTangency->attribute(SketchPlugin_Constraint::ENTITY_B())); - if (aRefAttrA->object() == aThisArc) - aRefAttrB->setObject(aTangFeature); - else - aRefAttrA->setObject(aTangFeature); - } else { - // Wait all constraints being removed, then send update events - static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED); - bool isDeleteFlushed = Events_Loop::loop()->isFlushed(aDeleteEvent); - if (isDeleteFlushed) - Events_Loop::loop()->setFlushed(aDeleteEvent, false); - // Remove all obtained constraints which use current arc, because - // there is no information which of them were used to build tangency arc. - DocumentPtr aDoc = sketch()->document(); - std::set aFeaturesToBeRemoved; - for (aCIt = aCoincidence.begin(); aCIt != aCoincidence.end(); ++aCIt) - aFeaturesToBeRemoved.insert(*aCIt); - for (aTIt = aTangency.begin(); aTIt != aTangency.end(); ++aTIt) - aFeaturesToBeRemoved.insert(*aTIt); - ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved); - // Send events to update the sub-features by the solver. - if (isDeleteFlushed) - Events_Loop::loop()->setFlushed(aDeleteEvent, true); - else - Events_Loop::loop()->flush(aDeleteEvent); - - // Wait all constraints being created, then send update events - static Events_ID aCreateEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); - bool isCreateFlushed = Events_Loop::loop()->isFlushed(aCreateEvent); - if (isCreateFlushed) - Events_Loop::loop()->setFlushed(aCreateEvent, false); - - // Create new constraints - FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); - aRefAttrA = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttrA->setAttr(aStartAttr); - aRefAttrB->setAttr(aTangPtAttr->attr()); - aConstraint->execute(); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, aCreateEvent); - - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID()); - aRefAttrA = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttrB = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttrA->setObject(aThisArc); - aRefAttrB->setObject(aTangFeature); - aConstraint->execute(); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, aCreateEvent); - - // Send events to update the sub-features by the solver. - if(isCreateFlushed) - Events_Loop::loop()->setFlushed(aCreateEvent, true); - else - Events_Loop::loop()->flush(aCreateEvent); - } + return boolean(REVERSED_ID())->value(); }