From: Artem Zhidkov Date: Fri, 22 May 2020 16:55:22 +0000 (+0300) Subject: Task #3230: Sketcher: create a curve passing through selected points or vertices... X-Git-Tag: V9_6_0a1~72 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=87447545000c44e455431ed0d401f850f373374e;p=modules%2Fshaper.git Task #3230: Sketcher: create a curve passing through selected points or vertices of a polyline * Implement the Curve Fitting feature. * Implement the de Boor scheme for periodic and non-periodic B-spline evaluation. * Implement interpolation and approximation modes of the feature. * Add creation of the control polygon. * Reordering points on the corresponding button click * Python API for this feature * User documentation --- diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.cpp index ad1850a08..fd98f26af 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.cpp @@ -24,46 +24,51 @@ #include #include +#include #include -#include -#include -#include -#include +#include +#include +#include #include +#include #include #include +#include +#include +#include +#include +#include -static void reorder(Handle(TColgp_HArray1OfPnt)& thePoints); - -//================================================================================================= GeomEdgePtr GeomAlgoAPI_CurveBuilder::edge(const std::list& thePoints, - const bool theIsClosed, - const bool theIsToReorder, - const GeomDirPtr& theStartTangent, - const GeomDirPtr& theEndTangent) + const bool thePeriodic, + const bool theIsToReorder, + const GeomDirPtr& theStartTangent, + const GeomDirPtr& theEndTangent) { - // Prepare points array - Handle(TColgp_HArray1OfPnt) aPoints = new TColgp_HArray1OfPnt(1, (int)thePoints.size()); - std::list::const_iterator anIt = thePoints.begin(); - for (int i = 1; anIt != thePoints.end(); anIt++, i++) { - GeomPointPtr aPoint = *anIt; - aPoints->SetValue(i, aPoint->impl()); - } + std::list aPointsCopy = thePoints; // If the curve to be closed - remove last point if it is too close to the first one - bool isClose = aPoints->First().Distance(aPoints->Last()) <= gp::Resolution(); - if (isClose && theIsClosed) { - aPoints->Resize(aPoints->Lower(), aPoints->Upper() - 1, Standard_True); + bool isClose = aPointsCopy.front()->distance(aPointsCopy.back()) <= gp::Resolution(); + if (isClose && thePeriodic) { + aPointsCopy.pop_back(); } // Reorder points if required if (theIsToReorder) { - reorder(aPoints); + reorderPoints(aPointsCopy); + } + + // Prepare points array + Handle(TColgp_HArray1OfPnt) aPoints = new TColgp_HArray1OfPnt(1, (int)aPointsCopy.size()); + std::list::const_iterator anIt = aPointsCopy.begin(); + for (int i = 1; anIt != aPointsCopy.end(); anIt++, i++) { + GeomPointPtr aPoint = *anIt; + aPoints->SetValue(i, aPoint->impl()); } // Initialize interpolator - GeomAPI_Interpolate anInterp(aPoints, theIsClosed, gp::Resolution()); + GeomAPI_Interpolate anInterp(aPoints, thePeriodic, gp::Resolution()); // Assign tangents if defined if (theStartTangent && theEndTangent) { @@ -92,54 +97,114 @@ GeomEdgePtr GeomAlgoAPI_CurveBuilder::edge(const std::list& thePoi return aResultShape; } -//================ Auxiliary functions ======================================================== -void reorder(Handle(TColgp_HArray1OfPnt)& thePoints) +GeomEdgePtr GeomAlgoAPI_CurveBuilder::approximate(const std::list& thePoints, + const bool thePeriodic, + const double thePrecision) +{ + // Prepare points array to be able to build a surface. + // This surface is based on two sets of points: the first is an original and + // the second is shifted along orthogonal direction. + // This is a workaround, because GeomAPI_PointsToBSpline algorithm cannot produce + // the periodic curve, but GeomAPI_PointsToBSplineSurface can. + TColgp_Array2OfPnt aPoints(1, (int)thePoints.size(), 1, 2); + gp_Pnt aPlaneBase[3]; // base points to calculate the normal direction + int aNbPlanePoints = 0; + gp_Dir aNormal; + std::list::const_iterator anIt = thePoints.begin(); + for (int i = 1; anIt != thePoints.end(); anIt++, i++) { + const gp_Pnt& aPoint = (*anIt)->impl(); + aPoints.SetValue(i, 1, aPoint); + aPoints.SetValue(i, 2, aPoint); + if (aNbPlanePoints < 3) { + if (aNbPlanePoints == 0 || + aPoint.SquareDistance(aPlaneBase[0]) > Precision::SquareConfusion()) + aPlaneBase[aNbPlanePoints++] = aPoint; + if (aNbPlanePoints == 3) { + gp_Vec aVec12(aPlaneBase[0], aPlaneBase[1]); + gp_Vec aVec13(aPlaneBase[0], aPlaneBase[2]); + if (aVec12.CrossSquareMagnitude(aVec13) > Precision::SquareConfusion()) + aNormal = gp_Dir(aVec12 ^ aVec13); + else + --aNbPlanePoints; + } + } + } + if (aNbPlanePoints < 3) + aNormal = gp::DZ(); + // shifted points + for (int i = aPoints.LowerRow(); i <= aPoints.UpperRow(); i++) + aPoints.ChangeValue(i, 2).ChangeCoord() += aNormal.XYZ(); + + // If the curve to be closed - remove last point if it is too close to the first one + bool isClose = aPoints.Value(aPoints.LowerRow(), 1).Distance( + aPoints.Value(aPoints.UpperRow(), 1)) <= gp::Resolution(); + if (isClose && thePeriodic) { + aPoints.Resize(aPoints.LowerRow(), aPoints.UpperRow() - 1, + aPoints.LowerCol(), aPoints.UpperCol(), Standard_True); + } + + // Initialize and perform approximator + static const Standard_Integer DEGREE_MIN = 3; + static const Standard_Integer DEGREE_MAX = 8; + GeomAPI_PointsToBSplineSurface anApprox; + anApprox.Init(aPoints, Approx_ChordLength, DEGREE_MIN, DEGREE_MAX, + GeomAbs_C2, thePrecision, thePeriodic); + + // Set result in form of edge + TopoDS_Edge anEdge; + if (anApprox.IsDone()) { + // build a curve along U-direction of the surface + Handle(Geom_BSplineSurface) aSurface = anApprox.Surface(); + Handle(Geom_Curve) aCurve = aSurface->VIso(aSurface->VKnots().First()); + + anEdge = BRepBuilderAPI_MakeEdge(aCurve).Edge(); + } + + GeomEdgePtr aResultShape(new GeomAPI_Edge); + aResultShape->setImpl(new TopoDS_Shape(anEdge)); + + return aResultShape; +} + +void GeomAlgoAPI_CurveBuilder::reorderPoints(std::list& thePoints) { - if (thePoints->Length() < 3) { + if (thePoints.size() < 3) { return; } - int aNbPoints = thePoints->Length(); int aNbDup = 0; - gp_Pnt aPrevPnt = thePoints->Value(1); - for (int i = 1; i < aNbPoints; i++) { - gp_Pnt aPnt = thePoints->Value(i); - int aNearest = 0; + std::list::iterator aPIt = thePoints.begin(); + GeomPointPtr aPrevPnt = *aPIt; + for (; aPIt != thePoints.end(); ++aPIt) { + GeomPointPtr aPnt = *aPIt; + std::list::iterator aNextIt = aPIt; + std::list::iterator aNearestIt = ++aNextIt; double aMinDist = RealLast(); - for (int j = i + 1; j <= aNbPoints; j++) { - double aDist = aPnt.SquareDistance(thePoints->Value(j)); + while (aNextIt != thePoints.end()) { + double aDist = aPnt->distance(*aNextIt); + if (aDist < Precision::Confusion()) { + // remove duplicates + std::list::iterator aRemoveIt = aNextIt++; + thePoints.erase(aRemoveIt); + continue; + } if (aDist < aMinDist && (aMinDist - aDist) > Precision::Confusion()) { - aNearest = j; + aNearestIt = aNextIt; aMinDist = aDist; } + ++aNextIt; } - if (aNearest > 0 && aNearest != i + 1) { + aNextIt = aPIt; ++aNextIt; + if (aNearestIt != aNextIt) { // Keep given order of points to use it in case of equidistant candidates // .--<---<--. // | | // o o o c o->o->o->o->n o o // | | | // i i+1 nearest - gp_Pnt aNearestPnt = thePoints->Value(aNearest); - for (int j = aNearest; j > i + 1; j--) { - thePoints->SetValue(j, thePoints->Value(j - 1)); - } - thePoints->SetValue(i + 1, aNearestPnt); - } - if (aPrevPnt.Distance(thePoints->Value(i + 1)) <= Precision::Confusion()) - aNbDup++; - else - aPrevPnt = thePoints->Value(i + 1); - } - - if (aNbDup > 0) { - Handle(TColgp_HArray1OfPnt) aTmpPoints = new TColgp_HArray1OfPnt(1, aNbPoints - aNbDup); - for (int j = 1, i = 1; i <= aNbPoints; i++) { - if (i == 1 || aPrevPnt.Distance(thePoints->Value(i)) > Precision::Confusion()) { - aTmpPoints->SetValue(j++, thePoints->Value(i)); - aPrevPnt = thePoints->Value(i); - } + GeomPointPtr aPointToMove = *aNearestIt; + thePoints.erase(aNearestIt); + thePoints.insert(aNextIt, aPointToMove); } - thePoints = aTmpPoints; } } \ No newline at end of file diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.h b/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.h index 2419422d7..8f5b65174 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_CurveBuilder.h @@ -27,23 +27,35 @@ /// \class GeomAlgoAPI_CurveBuilder /// \ingroup DataAlgo -/// \brief Allows to create interpolation curve. +/// \brief Allows to create a curve by the list of point. class GeomAlgoAPI_CurveBuilder { public: /// \brief Creates an interpolation curve from points. /// \param[in] thePoints list of points. - /// \param[in] theIsClosed defines whether the curve to be closed. + /// \param[in] thePeriodic defines whether the curve to be periodic. /// \param[in] theIsToReorder defines whether to change the order of points to construct /// the shortest curve. /// \param[in] theStartTangent vector tangent to the start of curve. /// \param[in] theEndTangent vector tangent to the end of curve. /// \return Interpolation curve (edge). Empty in case of error or bad input. GEOMALGOAPI_EXPORT static GeomEdgePtr edge(const std::list& thePoints, - const bool theIsClosed, + const bool thePeriodic, const bool theIsToReorder, const GeomDirPtr& theStartTangent, const GeomDirPtr& theEndTangent); + + /// \brief Approximate the given points by a curve. + /// \param[in] thePoints list of points. + /// \param[in] thePeriodic defines whether the curve to be periodic. + /// \param[in] thePrecision how close the curve should be to the points. + /// \return Apporimation curve (edge). Empty in case of error or bad input. + GEOMALGOAPI_EXPORT static GeomEdgePtr approximate(const std::list& thePoints, + const bool thePeriodic, + const double thePrecision); + + /// \brief Reoder the list of points to get a polyline of minimal length + GEOMALGOAPI_EXPORT static void reorderPoints(std::list& thePoints); }; #endif diff --git a/src/ModelAPI/ModelAPI_Feature.h b/src/ModelAPI/ModelAPI_Feature.h index 927f8de32..edf117fc2 100644 --- a/src/ModelAPI/ModelAPI_Feature.h +++ b/src/ModelAPI/ModelAPI_Feature.h @@ -198,6 +198,11 @@ class ModelAPI_Feature : public ModelAPI_Object { return data()->refattr(theID); } + /// Returns the refattrlist attribute by the identifier + inline std::shared_ptr refattrlist(const std::string& theID) + { + return data()->refattrlist(theID); + } /// Returns the reference attribute by the identifier inline std::shared_ptr reference(const std::string& theID) { diff --git a/src/ModuleBase/ModuleBase_WidgetOptionalBox.cpp b/src/ModuleBase/ModuleBase_WidgetOptionalBox.cpp index 656bb7309..63cb21479 100644 --- a/src/ModuleBase/ModuleBase_WidgetOptionalBox.cpp +++ b/src/ModuleBase/ModuleBase_WidgetOptionalBox.cpp @@ -54,6 +54,7 @@ ModuleBase_WidgetOptionalBox::ModuleBase_WidgetOptionalBox(QWidget* theParent, myHaveFrame = theData->getBooleanAttribute("has_frame", true); myEnableOnCheck = theData->getBooleanAttribute("enable_on_check", true); + myAlwaysShowTitle = theData->getBooleanAttribute("show_title", false); bool isChecked = theData->getBooleanAttribute(ATTR_DEFAULT, false); setDefaultValue(isChecked ? "true" : "false"); @@ -201,7 +202,10 @@ void ModuleBase_WidgetOptionalBox::createControl(const OptionType& theType) myCheckBoxLayout = new QHBoxLayout(myCheckBoxFrame); ModuleBase_Tools::adjustMargins(myCheckBoxLayout); - myCheckBox = new QCheckBox(myCheckBoxFrame); + if (myAlwaysShowTitle) + myCheckBox = new QCheckBox(translate(myGroupTitle), myCheckBoxFrame); + else + myCheckBox = new QCheckBox(myCheckBoxFrame); myCheckBox->setChecked(getDefaultValue() == "true"); myCheckBoxLayout->addWidget(myCheckBox); diff --git a/src/ModuleBase/ModuleBase_WidgetOptionalBox.h b/src/ModuleBase/ModuleBase_WidgetOptionalBox.h index 620fe6fe7..e819d73b9 100644 --- a/src/ModuleBase/ModuleBase_WidgetOptionalBox.h +++ b/src/ModuleBase/ModuleBase_WidgetOptionalBox.h @@ -136,6 +136,7 @@ private: bool myHaveFrame; bool myEnableOnCheck; + bool myAlwaysShowTitle; }; #endif /* ModuleBase_WidgetOptionalBox_H_ */ diff --git a/src/SketchAPI/SketchAPI.i b/src/SketchAPI/SketchAPI.i index 03bb90f3e..d46066e6c 100644 --- a/src/SketchAPI/SketchAPI.i +++ b/src/SketchAPI/SketchAPI.i @@ -51,6 +51,8 @@ %feature("kwargs") SketchAPI_Ellipse::construction; %feature("kwargs") SketchAPI_EllipticArc::construction; %feature("kwargs") SketchAPI_Sketch::addSpline; +%feature("kwargs") SketchAPI_Sketch::addInterpolation; +%feature("kwargs") SketchAPI_Sketch::addApproximation; %feature("kwargs") SketchAPI_Sketch::setAngle; // shared pointers diff --git a/src/SketchAPI/SketchAPI_Sketch.cpp b/src/SketchAPI/SketchAPI_Sketch.cpp index 9bdecc85f..a4a9bec25 100644 --- a/src/SketchAPI/SketchAPI_Sketch.cpp +++ b/src/SketchAPI/SketchAPI_Sketch.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -793,6 +794,68 @@ std::shared_ptr SketchAPI_Sketch::addSpline( return aBSpline; } +//-------------------------------------------------------------------------------------- +static std::shared_ptr buildInterpolation( + const CompositeFeaturePtr& theSketch, + const FeaturePtr& theCurveFittingFeature, + const std::list& points, + const bool periodic, + const bool closed) +{ + AttributeBooleanPtr aPeriodicAttr = + theCurveFittingFeature->boolean(SketchPlugin_CurveFitting::PERIODIC_ID()); + fillAttribute(periodic, aPeriodicAttr); + AttributeBooleanPtr aClosedAttr = + theCurveFittingFeature->boolean(SketchPlugin_CurveFitting::CLOSED_ID()); + fillAttribute(closed, aClosedAttr); + AttributeRefAttrListPtr aPointsAttr = + theCurveFittingFeature->refattrlist(SketchPlugin_CurveFitting::POINTS_ID()); + fillAttribute(points, aPointsAttr); + apply(); // to execute and kill the macro-feature + + // find created B-spline feature + BSplinePtr aBSpline; + const std::string& aKindToFind = + periodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID(); + int aNbSubs = theSketch->numberOfSubs(); + for (int anIndex = aNbSubs - 1; anIndex >= 0; --anIndex) { + FeaturePtr aFeature = theSketch->subFeature(anIndex); + if (aFeature->getKind() == aKindToFind) { + aBSpline.reset(periodic ? new SketchAPI_BSplinePeriodic(aFeature) + : new SketchAPI_BSpline(aFeature)); + aBSpline->execute(); + break; + } + } + return aBSpline; +} + +std::shared_ptr SketchAPI_Sketch::addInterpolation( + const std::list& points, + const bool periodic, + const bool closed) +{ + CompositeFeaturePtr aSketch = compositeFeature(); + FeaturePtr anInterpFeature = aSketch->addFeature(SketchPlugin_CurveFitting::ID()); + anInterpFeature->string(SketchPlugin_CurveFitting::TYPE_ID()) + ->setValue(SketchPlugin_CurveFitting::TYPE_INTERPOLATION_ID()); + return buildInterpolation(aSketch, anInterpFeature, points, periodic, closed); +} + +std::shared_ptr SketchAPI_Sketch::addApproximation( + const std::list& points, + const ModelHighAPI_Double& precision, + const bool periodic, + const bool closed) +{ + CompositeFeaturePtr aSketch = compositeFeature(); + FeaturePtr anInterpFeature = aSketch->addFeature(SketchPlugin_CurveFitting::ID()); + anInterpFeature->string(SketchPlugin_CurveFitting::TYPE_ID()) + ->setValue(SketchPlugin_CurveFitting::TYPE_APPROXIMATION_ID()); + fillAttribute(precision, anInterpFeature->real(SketchPlugin_CurveFitting::PRECISION_ID())); + return buildInterpolation(aSketch, anInterpFeature, points, periodic, closed); +} + //-------------------------------------------------------------------------------------- std::shared_ptr SketchAPI_Sketch::addProjection( const ModelHighAPI_Selection & theExternalFeature, diff --git a/src/SketchAPI/SketchAPI_Sketch.h b/src/SketchAPI/SketchAPI_Sketch.h index 319884404..57d424760 100644 --- a/src/SketchAPI/SketchAPI_Sketch.h +++ b/src/SketchAPI/SketchAPI_Sketch.h @@ -28,13 +28,13 @@ #include #include +#include #include #include #include //-------------------------------------------------------------------------------------- class ModelAPI_CompositeFeature; class ModelAPI_Object; -class ModelHighAPI_Double; class ModelHighAPI_Integer; class ModelHighAPI_RefAttr; class ModelHighAPI_Reference; @@ -338,6 +338,21 @@ public: const std::list& multiplicities = std::list(), const bool periodic = false); + /// Add interpolation feature + SKETCHAPI_EXPORT + std::shared_ptr addInterpolation( + const std::list& points, + const bool periodic = false, + const bool closed = false); + + /// Add approximation feature + SKETCHAPI_EXPORT + std::shared_ptr addApproximation( + const std::list& points, + const ModelHighAPI_Double& precision = ModelHighAPI_Double(1.e-3), + const bool periodic = false, + const bool closed = false); + /// Add projection SKETCHAPI_EXPORT std::shared_ptr addProjection( diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 2fd61c769..1d3306f1c 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -49,6 +49,7 @@ SET(PROJECT_HEADERS SketchPlugin_ConstraintRigid.h SketchPlugin_ConstraintTangent.h SketchPlugin_ConstraintVertical.h + SketchPlugin_CurveFitting.h SketchPlugin_Ellipse.h SketchPlugin_EllipticArc.h SketchPlugin_ExternalValidator.h @@ -103,6 +104,7 @@ SET(PROJECT_SOURCES SketchPlugin_ConstraintRigid.cpp SketchPlugin_ConstraintTangent.cpp SketchPlugin_ConstraintVertical.cpp + SketchPlugin_CurveFitting.cpp SketchPlugin_Ellipse.cpp SketchPlugin_EllipticArc.cpp SketchPlugin_ExternalValidator.cpp diff --git a/src/SketchPlugin/SketchPlugin_CurveFitting.cpp b/src/SketchPlugin/SketchPlugin_CurveFitting.cpp new file mode 100644 index 000000000..f775679bd --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_CurveFitting.cpp @@ -0,0 +1,386 @@ +// Copyright (C) 2020 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +static void convertTo3D(SketchPlugin_Sketch* theSketch, + const AttributeRefAttrListPtr& theAttribute, + bool theClosedButNotPeriodic, + std::list& thePoints); + +static GeomEdgePtr buildInterpolationCurve(SketchPlugin_Sketch* theSketch, + AttributeRefAttrListPtr thePoints, + bool thePeriodic, + bool theClosed); +static GeomEdgePtr buildApproximationCurve(SketchPlugin_Sketch* theSketch, + AttributeRefAttrListPtr thePoints, + double thePrecision, + bool thePeriodic, + bool theClosed); + + +SketchPlugin_CurveFitting::SketchPlugin_CurveFitting() + : SketchPlugin_SketchEntity() +{ +} + +void SketchPlugin_CurveFitting::initDerivedClassAttributes() +{ + data()->addAttribute(POINTS_ID(), ModelAPI_AttributeRefAttrList::typeId()); + + data()->addAttribute(TYPE_ID(), ModelAPI_AttributeString::typeId()); + + data()->addAttribute(PRECISION_ID(), ModelAPI_AttributeDouble::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PRECISION_ID()); + + data()->addAttribute(NEED_CONTROL_POLYGON_ID(), ModelAPI_AttributeBoolean::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), + NEED_CONTROL_POLYGON_ID()); + + data()->addAttribute(PERIODIC_ID(), ModelAPI_AttributeBoolean::typeId()); + data()->addAttribute(CLOSED_ID(), ModelAPI_AttributeBoolean::typeId()); +} + +void SketchPlugin_CurveFitting::execute() +{ + FeaturePtr aBSpline = createBSplineFeature(); + // create control polygon + AttributeBooleanPtr aNeedControlPoly = boolean(NEED_CONTROL_POLYGON_ID()); + if (aNeedControlPoly && aNeedControlPoly->isInitialized() && aNeedControlPoly->value()) { + bool isPeriodic = boolean(PERIODIC_ID())->value(); + std::list aControlPoles; + SketchPlugin_MacroBSpline::createControlPolygon(aBSpline, isPeriodic, aControlPoles); + } + // constraints for the selected points + createConstraints(aBSpline); +} + +FeaturePtr SketchPlugin_CurveFitting::createBSplineFeature() +{ + // create transient curve if not created yet + if (!myTransientResult) { + getAISObject(AISObjectPtr()); + if (!myTransientResult) + return FeaturePtr(); + } + + SketchPlugin_Sketch* aSketch = sketch(); + if (!aSketch) + return FeaturePtr(); + + bool isPeriodic = boolean(PERIODIC_ID())->value(); + + FeaturePtr aBSplineFeature = aSketch->addFeature( + isPeriodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID()); + + // Convert transient edge to B-spline curve. + GeomCurvePtr aCurve = std::make_shared(myTransientResult); + std::shared_ptr aBSpline = std::make_shared(aCurve); + + // Fill attributes of B-spline feature: + bool aWasBlocked = aBSplineFeature->data()->blockSendAttributeUpdated(true, false); + // 1. Degree + aBSplineFeature->integer(SketchPlugin_BSplineBase::DEGREE_ID())->setValue(aBSpline->degree()); + // 2. Poles + std::list aPoles = aBSpline->poles(); + AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast( + aBSplineFeature->attribute(SketchPlugin_BSplineBase::POLES_ID())); + aPolesAttr->setSize((int)aPoles.size()); + int anIndex = 0; + for (auto it = aPoles.begin(); it != aPoles.end(); ++it, ++anIndex) + aPolesAttr->setPnt(anIndex, aSketch->to2D(*it)); + // 3. Weights + std::list aWeights = aBSpline->weights(); + AttributeDoubleArrayPtr aWeightsAttr = + aBSplineFeature->data()->realArray(SketchPlugin_BSplineBase::WEIGHTS_ID()); + if (aWeights.empty()) { + aWeightsAttr->setSize((int)aPoles.size()); + for (anIndex = 0; anIndex < (int)aPoles.size(); ++anIndex) + aWeightsAttr->setValue(anIndex, 1.0); + } + else { + aWeightsAttr->setSize((int)aWeights.size()); + anIndex = 0; + for (auto it = aWeights.begin(); it != aWeights.end(); ++it, ++anIndex) + aWeightsAttr->setValue(anIndex, *it); + } + // 4. Knots (normalized from 0 to 1) + std::list aKnots = aBSpline->knots(); + AttributeDoubleArrayPtr aKnotsAttr = + aBSplineFeature->data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID()); + aKnotsAttr->setSize((int)aKnots.size()); + double aFirstKnot = aKnots.front(); + double aLastKnot = aKnots.back(); + anIndex = 0; + for (auto it = aKnots.begin(); it != aKnots.end(); ++it, ++anIndex) + aKnotsAttr->setValue(anIndex, (*it - aFirstKnot) / (aLastKnot - aFirstKnot)); + // 5. Multiplicities + std::list aMults = aBSpline->mults(); + AttributeIntArrayPtr aMultsAttr = + aBSplineFeature->data()->intArray(SketchPlugin_BSplineBase::MULTS_ID()); + aMultsAttr->setSize((int)aMults.size()); + anIndex = 0; + for (auto it = aMults.begin(); it != aMults.end(); ++it, ++anIndex) + aMultsAttr->setValue(anIndex, *it); + + if (!isPeriodic) { + AttributePoint2DPtr aStartPoint = std::dynamic_pointer_cast( + aBSplineFeature->attribute(SketchPlugin_BSpline::START_ID())); + aStartPoint->setValue(aPolesAttr->pnt(0)); + + AttributePoint2DPtr aEndPoint = std::dynamic_pointer_cast( + aBSplineFeature->attribute(SketchPlugin_BSpline::END_ID())); + aEndPoint->setValue(aPolesAttr->pnt(aPolesAttr->size() - 1)); + } + + aBSplineFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue( + boolean(AUXILIARY_ID())->value()); + + aBSplineFeature->data()->blockSendAttributeUpdated(aWasBlocked); + + aBSplineFeature->execute(); + + return aBSplineFeature; +} + +void SketchPlugin_CurveFitting::createConstraints(FeaturePtr theProducedFeature) +{ + if (!theProducedFeature) + return; + + SketchPlugin_Sketch* aSketch = sketch(); + ResultPtr aResult = theProducedFeature->lastResult(); + bool isPeriodic = boolean(PERIODIC_ID())->value(); + bool isClosed = boolean(CLOSED_ID())->value(); + bool isApproximation = string(TYPE_ID())->value() == TYPE_APPROXIMATION_ID(); + + AttributeRefAttrListPtr aPointsAttr = refattrlist(POINTS_ID()); + std::list > aPointsList = aPointsAttr->list(); + std::list >::iterator aLastIt = --aPointsList.end(); + for (auto it = aPointsList.begin(); it != aPointsList.end(); ++it) { + AttributePtr anAttr = it->second; + if (!anAttr) { + // maybe the SketchPoint is selected + FeaturePtr aFeature = ModelAPI_Feature::feature(it->first); + if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID()) + anAttr = aFeature->attribute(SketchPlugin_Point::COORD_ID()); + else + continue; + } + + if (!isPeriodic && it == aPointsList.begin()) { + SketchPlugin_Tools::createConstraintAttrAttr(aSketch, + SketchPlugin_ConstraintCoincidence::ID(), + anAttr, theProducedFeature->attribute(SketchPlugin_BSpline::START_ID())); + if (isClosed) { + // end of B-spline curve should be coincident with the first selected point + SketchPlugin_Tools::createConstraintAttrAttr(aSketch, + SketchPlugin_ConstraintCoincidence::ID(), + anAttr, theProducedFeature->attribute(SketchPlugin_BSpline::END_ID())); + } + } + else if (!isPeriodic && !isClosed && it == aLastIt) { + SketchPlugin_Tools::createConstraintAttrAttr(aSketch, + SketchPlugin_ConstraintCoincidence::ID(), + anAttr, theProducedFeature->attribute(SketchPlugin_BSpline::END_ID())); + } + else if (!isApproximation) { + SketchPlugin_Tools::createConstraintAttrObject(aSketch, + SketchPlugin_ConstraintCoincidence::ID(), + anAttr, aResult); + } + } +} + +bool SketchPlugin_CurveFitting::customAction(const std::string& theActionId) +{ + bool isOk = true; + if (theActionId == REORDER_POINTS_ACTION_ID()) { + reorderPoints(); + } else { + static const std::string MESSAGE("Error: Feature \"%1\" does not support action \"%2\"."); + Events_InfoMessage("SketchPlugin_CurveFitting", MESSAGE) + .arg(getKind()).arg(theActionId).send(); + isOk = false; + } + return isOk; +} + + +void SketchPlugin_CurveFitting::reorderPoints() +{ + AttributeRefAttrListPtr aPointsAttr = refattrlist(POINTS_ID()); + bool isPeriodic = boolean(PERIODIC_ID())->value(); + bool isClosed = boolean(CLOSED_ID())->value(); + + std::list aCoordinates; + convertTo3D(sketch(), aPointsAttr, !isPeriodic && isClosed, aCoordinates); + + // to keep mapping between points and attributes + std::map > aMap; + std::list > aPointsList = aPointsAttr->list(); + bool isPointAdded = aCoordinates.size() != aPointsList.size(); + std::list::iterator aCoordIt = aCoordinates.begin(); + std::list >::iterator anAttrIt = aPointsList.begin(); + for (; aCoordIt != aCoordinates.end() && anAttrIt != aPointsList.end(); ++aCoordIt, ++anAttrIt) + aMap[*aCoordIt] = *anAttrIt; + + // reorder points + GeomAlgoAPI_CurveBuilder::reorderPoints(aCoordinates); + + // re-compose the attribute + bool aWasBlocked = data()->blockSendAttributeUpdated(true); + aPointsAttr->clear(); + for (aCoordIt = aCoordinates.begin(); aCoordIt != aCoordinates.end(); ++aCoordIt) { + const std::pair& aValue = aMap.at(*aCoordIt); + if (aValue.second) + aPointsAttr->append(aValue.second); + else + aPointsAttr->append(aValue.first); + } + data()->blockSendAttributeUpdated(aWasBlocked); +} + +AISObjectPtr SketchPlugin_CurveFitting::getAISObject(AISObjectPtr thePrevious) +{ + SketchPlugin_Sketch* aSketch = sketch(); + if (!aSketch) + return AISObjectPtr(); + + std::string aType = string(TYPE_ID())->value(); + if (aType == TYPE_INTERPOLATION_ID()) { + myTransientResult = buildInterpolationCurve(aSketch, + refattrlist(POINTS_ID()), + boolean(PERIODIC_ID())->value(), + boolean(CLOSED_ID())->value()); + } + else if (aType == TYPE_APPROXIMATION_ID()) { + myTransientResult = buildApproximationCurve(aSketch, + refattrlist(POINTS_ID()), + real(PRECISION_ID())->value(), + boolean(PERIODIC_ID())->value(), + boolean(CLOSED_ID())->value()); + } + if (!myTransientResult) + return AISObjectPtr(); + + AISObjectPtr anAIS = thePrevious; + if (!anAIS) + anAIS.reset(new GeomAPI_AISObject()); + anAIS->createShape(myTransientResult); + + // Modify attributes + SketchPlugin_Tools::customizeFeaturePrs(anAIS, boolean(AUXILIARY_ID())->value()); + + return anAIS; +} + + +// =============================== Auxiliary functions ==================================== + +void convertTo3D(SketchPlugin_Sketch* theSketch, + const AttributeRefAttrListPtr& theAttribute, + bool theClosedButNotPeriodic, + std::list& thePoints) +{ + std::list > aPointsList = theAttribute->list(); + for (auto it = aPointsList.begin(); it != aPointsList.end(); ++it) { + AttributePtr anAttr = it->second; + if (!anAttr) { + // maybe the SketchPoint is selected + FeaturePtr aFeature = ModelAPI_Feature::feature(it->first); + if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID()) + anAttr = aFeature->attribute(SketchPlugin_Point::COORD_ID()); + else { + thePoints.clear(); + break; + } + } + + AttributePoint2DPtr aPoint = std::dynamic_pointer_cast(anAttr); + if (aPoint) { + GeomPointPtr aPnt3D = theSketch->to3D(aPoint->x(), aPoint->y()); + thePoints.push_back(aPnt3D); + } + } + + if (theClosedButNotPeriodic && !thePoints.empty() && + thePoints.front()->distance(thePoints.back()) > 1.e-7) + thePoints.push_back(thePoints.front()); // close the curve +} + +GeomEdgePtr buildInterpolationCurve(SketchPlugin_Sketch* theSketch, + AttributeRefAttrListPtr thePoints, + bool thePeriodic, + bool theClosed) +{ + std::list aCoordinates; + convertTo3D(theSketch, thePoints, !thePeriodic && theClosed, aCoordinates); + + GeomEdgePtr aResult; + if (aCoordinates.size() > 1) { + static const bool isReorder = false; + static GeomDirPtr aStartEndDir; + aResult = GeomAlgoAPI_CurveBuilder::edge(aCoordinates, thePeriodic, + isReorder, aStartEndDir, aStartEndDir); + } + return aResult; +} + +GeomEdgePtr buildApproximationCurve(SketchPlugin_Sketch* theSketch, + AttributeRefAttrListPtr thePoints, + double thePrecision, + bool thePeriodic, + bool theClosed) +{ + std::list aCoordinates; + convertTo3D(theSketch, thePoints, !thePeriodic && theClosed, aCoordinates); + + GeomEdgePtr aResult; + if (aCoordinates.size() > 1) + aResult = GeomAlgoAPI_CurveBuilder::approximate(aCoordinates, thePeriodic, thePrecision); + return aResult; +} diff --git a/src/SketchPlugin/SketchPlugin_CurveFitting.h b/src/SketchPlugin/SketchPlugin_CurveFitting.h new file mode 100644 index 000000000..d7c8b64b1 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_CurveFitting.h @@ -0,0 +1,154 @@ +// Copyright (C) 2020 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 +// + +#ifndef SketchPlugin_CurveFitting_H_ +#define SketchPlugin_CurveFitting_H_ + +#include +#include + +#include + +class GeomAPI_Edge; + +/**\class SketchPlugin_CurveFitting + * \ingroup Plugins + * \brief Feature for creation of the new B-spline curve in sketch + * as an interpolation or an approximation of a list of points. + */ +class SketchPlugin_CurveFitting : public SketchPlugin_SketchEntity, + public GeomAPI_IPresentable +{ +public: + /// Interpolation macro feature kind + inline static const std::string& ID() + { + static const std::string ID("SketchCurveFitting"); + return ID; + } + + /// list of selected points + inline static const std::string& POINTS_ID() + { + static const std::string ID("points"); + return ID; + } + + /// attribute for the periodic flag + inline static const std::string& PERIODIC_ID() + { + static const std::string ID("periodic"); + return ID; + } + + /// attribute for the closed flag + inline static const std::string& CLOSED_ID() + { + static const std::string ID("closed"); + return ID; + } + + /// attribute for the flag of creation a control polygon + inline static const std::string& NEED_CONTROL_POLYGON_ID() + { + static const std::string ID("need_control_poly"); + return ID; + } + + /// attribute for the type of the operation + inline static const std::string& TYPE_ID() + { + static const std::string ID("type"); + return ID; + } + + /// value for the type of operation + inline static const std::string& TYPE_INTERPOLATION_ID() + { + static const std::string ID("interpolation_type"); + return ID; + } + + /// value for the type of operation + inline static const std::string& TYPE_APPROXIMATION_ID() + { + static const std::string ID("approximation_type"); + return ID; + } + + /// attribute for the precision of the approximation + inline static const std::string& PRECISION_ID() + { + static const std::string ID("precision"); + return ID; + } + + /// attribute for the closed flag + inline static const std::string& REORDER_POINTS_ACTION_ID() + { + static const std::string ID("reorder_points"); + return ID; + } + + /// Returns the kind of a feature + SKETCHPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = SketchPlugin_CurveFitting::ID(); + return MY_KIND; + } + + /// Returns the AIS preview + virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious); + + /// Creates a new part document if needed + SKETCHPLUGIN_EXPORT virtual void execute(); + + /// Reimplemented from ModelAPI_Feature::isMacro(). + /// \returns true + SKETCHPLUGIN_EXPORT virtual bool isMacro() const {return true;}; + + SKETCHPLUGIN_EXPORT virtual bool isPreviewNeeded() const {return false;}; + + /// Performs some functionality by action id. + /// \param[in] theAttributeId action key id. + /// \return false in case if action not performed. + SKETCHPLUGIN_EXPORT virtual bool customAction(const std::string& theActionId); + + /// Use plugin manager for features creation + SketchPlugin_CurveFitting(); + +protected: + /// \brief Initializes attributes of derived class. + virtual void initDerivedClassAttributes(); + +private: + /// \brief Create a feature, which passes through the selected points + FeaturePtr createBSplineFeature(); + + /// \brief Create coincidence constraints between selected points and the produced curve. + void createConstraints(FeaturePtr theProducedFeature); + + /// \brief Reorder point to compose the polyline of the minimal length + void reorderPoints(); + +private: + std::shared_ptr myTransientResult; ///< Interpolation curve +}; + +#endif diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp index 48158f1a8..3d3ab4c53 100644 --- a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp @@ -84,7 +84,7 @@ void SketchPlugin_MacroBSpline::execute() if (boolean(CONTROL_POLYGON_ID())->value()) { std::list aControlPoles; - createControlPolygon(aBSpline, aControlPoles); + createControlPolygon(aBSpline, myIsPeriodic, aControlPoles); constraintsForPoles(aControlPoles); } } @@ -147,6 +147,7 @@ FeaturePtr SketchPlugin_MacroBSpline::createBSplineFeature() } void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline, + bool thePeriodic, std::list& thePoles) { AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast( @@ -158,7 +159,7 @@ void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline, // segments for (int index = 1; index < aSize; ++index) createAuxiliarySegment(aPoles, index - 1, index); - if (myIsPeriodic) { + if (thePeriodic) { // additional segment to close the control polygon createAuxiliarySegment(aPoles, aSize - 1, 0); } diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.h b/src/SketchPlugin/SketchPlugin_MacroBSpline.h index 9ecf63d49..1c3aca6d7 100644 --- a/src/SketchPlugin/SketchPlugin_MacroBSpline.h +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.h @@ -108,7 +108,12 @@ protected: private: FeaturePtr createBSplineFeature(); - void createControlPolygon(FeaturePtr theBSpline, std::list& thePoles); + /// Create control polygon for the B-spline and returns the list of its poles + static void createControlPolygon(FeaturePtr theBSpline, + bool thePeriodic, + std::list& thePoles); + + /// Create additional coincidences if other features were selected while creating the B-spline void constraintsForPoles(const std::list& thePoles); /// Create Point feature coincident with the B-spline pole @@ -124,6 +129,7 @@ private: const int thePoleIndex1, const int thePoleIndex2 = -1); friend class SketchPlugin_BSplineBase; + friend class SketchPlugin_CurveFitting; private: std::list myKnots; @@ -133,9 +139,9 @@ private: }; -/**\class SketchPlugin_MacroBSpline +/**\class SketchPlugin_MacroBSplinePeriodic * \ingroup Plugins -* \brief Feature for creation of the new B-spline in Sketch. +* \brief Feature for creation of the new periodic B-spline in Sketch. */ class SketchPlugin_MacroBSplinePeriodic : public SketchPlugin_MacroBSpline { diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index 490a1d77d..a2f0de04a 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include @@ -153,6 +154,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin() new SketchPlugin_MultiRotationAngleValidator); aFactory->registerValidator("SketchPlugin_BSplineValidator", new SketchPlugin_BSplineValidator); + aFactory->registerValidator("SketchPlugin_CurveFittingValidator", + new SketchPlugin_CurveFittingValidator); // register this plugin ModelAPI_Session::get()->registerPlugin(this); @@ -275,6 +278,8 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new SketchPlugin_EllipticArc); } else if (theFeatureID == SketchPlugin_MacroEllipticArc::ID()) { return FeaturePtr(new SketchPlugin_MacroEllipticArc); + } else if (theFeatureID == SketchPlugin_CurveFitting::ID()) { + return FeaturePtr(new SketchPlugin_CurveFitting); } else if (theFeatureID == SketchPlugin_SketchDrawer::ID()) { return FeaturePtr(new SketchPlugin_SketchDrawer); } else if (theFeatureID == SketchPlugin_SketchCopy::ID()) { @@ -356,6 +361,8 @@ std::shared_ptr SketchPlugin_Plugin aMsg->setState(SketchPlugin_MacroEllipticArc::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_ConstraintDistanceHorizontal::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_ConstraintDistanceVertical::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_CurveFitting::ID(), aHasSketchPlane); + // SketchRectangle is a python feature, so its ID is passed just as a string aMsg->setState("SketchRectangle", aHasSketchPlane); } diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index 22c0d2fe1..4a6cc4b25 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -31,6 +31,7 @@ #include "SketchPlugin_Ellipse.h" #include "SketchPlugin_EllipticArc.h" #include "SketchPlugin_Fillet.h" +#include "SketchPlugin_CurveFitting.h" #include "SketchPlugin_Line.h" #include "SketchPlugin_MacroArc.h" #include "SketchPlugin_MacroCircle.h" @@ -49,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -1893,3 +1895,22 @@ bool SketchPlugin_BSplineValidator::isValid(const AttributePtr& theAttribute, return true; } + +bool SketchPlugin_CurveFittingValidator::isValid(const FeaturePtr& theFeature, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + AttributeRefAttrListPtr aRefAttrList = + theFeature->refattrlist(SketchPlugin_CurveFitting::POINTS_ID()); + AttributeBooleanPtr aPeriodicAttr = + theFeature->boolean(SketchPlugin_CurveFitting::PERIODIC_ID()); + + // check number of selected entities + int aMinNbPoints = aPeriodicAttr->value() ? 3 : 2; + if (aRefAttrList->size() < aMinNbPoints) { + theError = "Not enough points selected. Need at least %1 points."; + theError.arg(aMinNbPoints); + return false; + } + return true; +} diff --git a/src/SketchPlugin/SketchPlugin_Validators.h b/src/SketchPlugin/SketchPlugin_Validators.h index 1f5a8b846..01c1f4f3a 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.h +++ b/src/SketchPlugin/SketchPlugin_Validators.h @@ -546,4 +546,20 @@ class SketchPlugin_BSplineValidator : public ModelAPI_AttributeValidator Events_InfoMessage& theError) const; }; +/**\class SketchPlugin_CurveFittingValidator + * \ingroup Validators + * \brief Validator for the selected vertices for the curve fitting feature. + */ +class SketchPlugin_CurveFittingValidator : public ModelAPI_FeatureValidator +{ +public: + //! returns true if number of selected points is greater than the minimal value + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError error message + virtual bool isValid(const std::shared_ptr& theFeature, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + #endif diff --git a/src/SketchPlugin/SketchPlugin_msg_en.ts b/src/SketchPlugin/SketchPlugin_msg_en.ts index 5dd2eaa01..aad453f54 100644 --- a/src/SketchPlugin/SketchPlugin_msg_en.ts +++ b/src/SketchPlugin/SketchPlugin_msg_en.ts @@ -1,6 +1,15 @@ + + + workshop + + Curve fitting + Curve fitting + + + Sketch:Model_FeatureValidator @@ -2262,4 +2271,121 @@ + + + + SketchCurveFitting + + Curve fitting + Curve fitting + + + Create curve passing through the points + Create curve passing through the points + + + Error: Feature "%1" does not support action "%2". + Error: Feature "%1" does not support action "%2". + + + + SketchCurveFitting:SketchPlugin_CurveFittingValidator + + Not enough points selected. Need at least %1 points. + Not enough points selected. Need at least %1 points. + + + + SketchCurveFitting:points + + Points + Points + + + Select points for curve fitting + Select points for curve fitting + + + Attribute "%1" is not initialized. + Select points for interpolation + + + + SketchCurveFitting:periodic + + Periodic + Periodic + + + Make curve periodic + Make curve periodic + + + + SketchCurveFitting:closed + + Closed + Closed + + + Make curve closed, but not periodic + Make curve closed, but not periodic + + + + SketchCurveFitting:type + + Interpolation + Interpolation + + + Approximation + Approximation + + + + SketchCurveFitting:precision + + Precision + Precision + + + Maximal distance from selected points to the curve + Maximal distance from selected points to the curve + + + + SketchCurveFitting:need_control_poly + + Create control polygon + Create control polygon + + + Specify if the control polygon should be created + Specify if the control polygon should be created + + + + SketchCurveFitting:reorder_points + + Reorder points + Reorder points + + + Sort selected points to minimize the distance heighbors + Sort selected points to minimize the distance heighbors + + + + SketchCurveFitting:Auxiliary + + Auxiliary + Auxiliary + + + Construction element + Construction element + + + diff --git a/src/SketchPlugin/SketchPlugin_msg_fr.ts b/src/SketchPlugin/SketchPlugin_msg_fr.ts index 86f682282..d509191c4 100644 --- a/src/SketchPlugin/SketchPlugin_msg_fr.ts +++ b/src/SketchPlugin/SketchPlugin_msg_fr.ts @@ -72,6 +72,10 @@ Horizontal Distance Distance horizontale + + Curve fitting + Courbe d'ajustement + Length Longueur @@ -4290,4 +4294,120 @@ + + + SketchCurveFitting + + Curve fitting + Courbe d'ajustement + + + Create curve passing through the points + Créer une courbe passant par les points + + + Error: Feature "%1" does not support action "%2". + Erreur: la fonctionnalité "% 1" ne prend pas en charge l'action "% 2". + + + + SketchCurveFitting:SketchPlugin_CurveFittingValidator + + Not enough points selected. Need at least %1 points. + Pas assez de points sélectionnés. Besoin d'au moins %1 points. + + + + SketchCurveFitting:points + + Points + Points + + + Select points for curve fitting + Sélectionner des points pour l'ajustement de courbe + + + Attribute "%1" is not initialized. + Sélectionner des points pour l'ajustement de courbe + + + + SketchCurveFitting:periodic + + Periodic + Périodique + + + Make curve periodic + Rendre la courbe périodique + + + + SketchCurveFitting:closed + + Closed + Fermé + + + Make curve closed, but not periodic + Rendre la courbe fermée, mais pas périodique + + + + SketchCurveFitting:type + + Interpolation + Interpolation + + + Approximation + Approximation + + + + SketchCurveFitting:precision + + Precision + Précision + + + Maximal distance from selected points to the curve + Distance maximale entre les points sélectionnés et la courbe + + + + SketchCurveFitting:need_control_poly + + Create control polygon + Créer un polygone de contrôle + + + Specify if the control polygon should be created + Précisez si le polygone de contrôle doit être créé + + + + SketchCurveFitting:reorder_points + + Reorder points + Réorganiser les points + + + Sort selected points to minimize the distance heighbors + Trier les points sélectionnés pour minimiser la distance des voisins + + + + SketchCurveFitting:Auxiliary + + Auxiliary + Auxiliaire + + + Construction element + Élément de construction + + + diff --git a/src/SketchPlugin/doc/TUI_curvefitting.rst b/src/SketchPlugin/doc/TUI_curvefitting.rst new file mode 100644 index 000000000..1c71914a4 --- /dev/null +++ b/src/SketchPlugin/doc/TUI_curvefitting.rst @@ -0,0 +1,23 @@ + + .. _tui_create_interpolation: + +Create interpolation curve +========================== + +.. literalinclude:: examples/interpolation.py + :linenos: + :language: python + +:download:`Download this script ` + + + .. _tui_create_approximation: + +Create approximation curve +========================== + +.. literalinclude:: examples/approximation.py + :linenos: + :language: python + +:download:`Download this script ` diff --git a/src/SketchPlugin/doc/curveFittingFeature.rst b/src/SketchPlugin/doc/curveFittingFeature.rst new file mode 100644 index 000000000..11376117b --- /dev/null +++ b/src/SketchPlugin/doc/curveFittingFeature.rst @@ -0,0 +1,89 @@ +.. |curvefitting.icon| image:: images/curvefitting.png + +Curve Fitting +============= + +The Curve Fitting is a tool to create a curve by a set of given points. + +To start this operation: + +#. select *Sketch - > Curve fitting* item in the Main Menu or +#. click |curvefitting.icon| **Curve fitting** button in the Sketch toolbar: + +There are 2 algorithms for the curve creation: + +- **Interpolation** - the curve passes exactly through the selected points; +- **Approximation** - curve passes near the selected points to some extent. + +Both algorithms have additional options: + +- **Periodic** - forces the created curve to be smooth periodic curve; +- **Closed** - produces closed, but non-periodic curve. As a result, it has the same start and end points, but it may be connected non-smoothly there; +- **Create control polygon** - if the fitting curve is a B-spline curve, this option will create it's control polygon. + + +Interpolation +""""""""""""" + +.. image:: images/curvefitting_panel_interpolation.png + :align: center + +Select the list of points to create a curve. The curve will pass directly through these points in the given order. + +Clicking the **Reorder points** button will change the order of selected points to minimize the distances between the neighbors. + +**TUI Command**: + +.. py:function:: Sketch_1.addInterpolation(points, periodic = False, closed = False) + + :param list: points for the curve. + :param boolean: flag to make the curve periodic. + :param boolean: flag to make the curve closed but not periodic. + :return: Created B-spline curve. + +Result +"""""" + +The created B-spline curve appears in the view. + +.. image:: images/curvefitting_interpolation_res.png + :align: center + +.. centered:: + Interpolation curve (non-closed, periodic and closed) + +**See Also** a sample TUI Script of :ref:`tui_create_interpolation` operation. + + +Approximation +""""""""""""" + +.. image:: images/curvefitting_panel_approximation.png + :align: center + +Select the list of points to create a curve and set the **Precision** value. The curve will pass not far than the precision of these points. + +Clicking the **Reorder points** button will change the order of selected points to minimize the distances between the neighbors. + +**TUI Command**: + +.. py:function:: Sketch_1.addApproximation(points, precision = 0.001, periodic = False, closed = False) + + :param list: points for the curve. + :param double: how close the curve should pass according to the points. + :param boolean: flag to make the curve periodic. + :param boolean: flag to make the curve closed but not periodic. + :return: Created B-spline curve. + +Result +"""""" + +The created B-spline curve appears in the view. + +.. image:: images/curvefitting_approximation_res.png + :align: center + +.. centered:: + Approximated curve (non-closed, periodic and closed) + +**See Also** a sample TUI Script of :ref:`tui_create_approximation` operation. diff --git a/src/SketchPlugin/doc/examples/approximation.py b/src/SketchPlugin/doc/examples/approximation.py new file mode 100644 index 000000000..f2725583a --- /dev/null +++ b/src/SketchPlugin/doc/examples/approximation.py @@ -0,0 +1,38 @@ +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchPoints +SketchPoint_1 = Sketch_1.addPoint(-70.28350515463917, 1.388316151202744) +SketchPoint_2 = Sketch_1.addPoint(-26.89862542955327, 51.7147766323024) +SketchPoint_3 = Sketch_1.addPoint(40.08762886597938, 32.27835051546391) +SketchPoint_4 = Sketch_1.addPoint(66.46563573883162, -29.8487972508591) + +### Create approximating curve +ApproximationPoints = [SketchPoint_1.coordinates(), + SketchPoint_2.coordinates(), + SketchPoint_3.coordinates(), + SketchPoint_4.coordinates()] +SketchBSpline_1 = Sketch_1.addApproximation(ApproximationPoints, precision = 30) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_1.startPoint()) +Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchBSpline_1.endPoint()) + +### Create periodic approximating curve +SketchBSpline_2 = Sketch_1.addApproximation(ApproximationPoints, precision = 30, periodic = True) + +### Create closed approximating curve +SketchBSpline_3 = Sketch_1.addApproximation(ApproximationPoints, precision = 30, closed = True) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_3.startPoint()) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_3.endPoint()) + +model.do() + +model.end() diff --git a/src/SketchPlugin/doc/examples/interpolation.py b/src/SketchPlugin/doc/examples/interpolation.py new file mode 100644 index 000000000..fc32ac4be --- /dev/null +++ b/src/SketchPlugin/doc/examples/interpolation.py @@ -0,0 +1,47 @@ +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() + +### Create Part +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() + +### Create Sketch +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) + +### Create SketchPoints +SketchPoint_1 = Sketch_1.addPoint(-70.28350515463917, 1.388316151202744) +SketchPoint_2 = Sketch_1.addPoint(-26.89862542955327, 51.7147766323024) +SketchPoint_3 = Sketch_1.addPoint(40.08762886597938, 32.27835051546391) +SketchPoint_4 = Sketch_1.addPoint(66.46563573883162, -29.8487972508591) + +### Create interpolation curve +InterpolationPoints = [SketchPoint_1.coordinates(), + SketchPoint_2.coordinates(), + SketchPoint_3.coordinates(), + SketchPoint_4.coordinates()] +SketchBSpline_1 = Sketch_1.addInterpolation(InterpolationPoints) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_1.startPoint()) +Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchBSpline_1.result()) +Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchBSpline_1.result()) +Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchBSpline_1.endPoint()) + +### Create periodic interpolation curve +SketchBSpline_2 = Sketch_1.addInterpolation(InterpolationPoints, periodic = True) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_2.result()) +Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchBSpline_2.result()) +Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchBSpline_2.result()) +Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchBSpline_2.result()) + +### Create closed interpolation curve +SketchBSpline_3 = Sketch_1.addInterpolation(InterpolationPoints, closed = True) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_3.startPoint()) +Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchBSpline_3.result()) +Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchBSpline_3.result()) +Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchBSpline_3.result()) +Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchBSpline_3.endPoint()) + +model.do() + +model.end() diff --git a/src/SketchPlugin/doc/images/curvefitting.png b/src/SketchPlugin/doc/images/curvefitting.png new file mode 100644 index 000000000..dea1f22ad Binary files /dev/null and b/src/SketchPlugin/doc/images/curvefitting.png differ diff --git a/src/SketchPlugin/doc/images/curvefitting_approximation_res.png b/src/SketchPlugin/doc/images/curvefitting_approximation_res.png new file mode 100644 index 000000000..007db89be Binary files /dev/null and b/src/SketchPlugin/doc/images/curvefitting_approximation_res.png differ diff --git a/src/SketchPlugin/doc/images/curvefitting_interpolation_res.png b/src/SketchPlugin/doc/images/curvefitting_interpolation_res.png new file mode 100644 index 000000000..dcde3f0b5 Binary files /dev/null and b/src/SketchPlugin/doc/images/curvefitting_interpolation_res.png differ diff --git a/src/SketchPlugin/doc/images/curvefitting_panel_approximation.png b/src/SketchPlugin/doc/images/curvefitting_panel_approximation.png new file mode 100644 index 000000000..2593baf90 Binary files /dev/null and b/src/SketchPlugin/doc/images/curvefitting_panel_approximation.png differ diff --git a/src/SketchPlugin/doc/images/curvefitting_panel_interpolation.png b/src/SketchPlugin/doc/images/curvefitting_panel_interpolation.png new file mode 100644 index 000000000..29cf328ad Binary files /dev/null and b/src/SketchPlugin/doc/images/curvefitting_panel_interpolation.png differ diff --git a/src/SketchPlugin/icons/curvefitting.png b/src/SketchPlugin/icons/curvefitting.png new file mode 100644 index 000000000..dea1f22ad Binary files /dev/null and b/src/SketchPlugin/icons/curvefitting.png differ diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index 8d111edbb..76800d383 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -9,6 +9,7 @@ SketchBSpline SketchMacroBSpline SketchMacroBSplinePeriodic SketchBSplinePeriodic SketchRectangle SketchProjection + SketchCurveFitting SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintDistanceHorizontal SketchConstraintDistanceVertical SketchConstraintParallel SketchConstraintPerpendicular SketchConstraintRigid SketchConstraintHorizontal SketchConstraintVertical @@ -701,6 +702,61 @@ obligatory="0" change_visual_attributes="true"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp index f69cd48b5..db330f5dd 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp @@ -22,65 +22,38 @@ #include #include -#include #include namespace GCS { -DeriVector2 BSplineImpl::Value(double u, double du, double* derivparam) -{ - if (!isCacheValid()) - rebuildCache(); - - std::shared_ptr aValue; - std::shared_ptr aDeriv; - myCurve->D1(u, aValue, aDeriv); - - // calculate the derivative on solver's parameter - std::shared_ptr aValueDeriv(new GeomAPI_Pnt2d(0.0, 0.0)); - bool hasParam = false; - std::list aPolesDeriv; - for (GCS::VEC_P::iterator anIt = poles.begin(); anIt != poles.end(); ++anIt) { - double x = 0.0, y = 0.0; - if (anIt->x == derivparam) { - x = 1.0; - hasParam = true; - } - else if (anIt->y == derivparam) { - y = 1.0; - hasParam = true; + static void periodicNormalization(double& theParam, double thePeriodStart, double thePeriodEnd) + { + double aPeriod = thePeriodEnd - thePeriodStart; + if (aPeriod > tolerance) { + theParam = std::max(thePeriodStart, + theParam + aPeriod * std::ceil((thePeriodStart - theParam) / aPeriod)); } - aPolesDeriv.push_back(GeomPnt2dPtr(new GeomAPI_Pnt2d(x, y))); - } - if (hasParam) { - // use non-periodic curve, because the most of B-spline coefficients are 0, - // thus, it is not necessary to keep original knots and multiplicities to get correct value - std::shared_ptr aCurveDeriv( - new GeomAPI_BSpline2d(degree, aPolesDeriv, myCachedWeights)); - aCurveDeriv->D0(u, aValueDeriv); } - return DeriVector2(aValue->x(), aValue->y(), - aValueDeriv->x() + aDeriv->x() * du, aValueDeriv->y() + aDeriv->y() * du); + +DeriVector2 BSplineImpl::Value(double u, double du, double* derivparam) +{ + DeriVector2 value, deriv; + d1(u, derivparam, value, deriv); + return value.sum(GCS::DeriVector2(0., 0., deriv.x, deriv.y).mult(du)); } DeriVector2 BSplineImpl::CalculateNormal(Point &p, double* derivparam) { - if (!isCacheValid()) - rebuildCache(); - double u = 0.0; - if (!myCurve->parameter(GeomPnt2dPtr(new GeomAPI_Pnt2d(*p.x, *p.y)), 1e100, u)) + if (!parameter(p, u)) return DeriVector2(); - std::shared_ptr aValue; - std::shared_ptr aDeriv; - myCurve->D1(u, aValue, aDeriv); - - DeriVector2 norm(aDeriv->x(), aDeriv->y(), 0.0, 0.0); - return norm.rotate90ccw(); + DeriVector2 value, deriv; + d1(u, derivparam, value, deriv); + return deriv.rotate90ccw(); } BSplineImpl* BSplineImpl::Copy() @@ -88,49 +61,120 @@ BSplineImpl* BSplineImpl::Copy() return new BSplineImpl(*this); } +void BSplineImpl::d1(double theU, + double* theDerivParam, + GCS::DeriVector2& theValue, + GCS::DeriVector2& theDerivative) +{ + int aSpan = spanIndex(theU); + std::vector aPoles; + std::vector aWeights; + spanPolesAndWeights(aSpan, theDerivParam, aPoles, aWeights); + performDeBoor(theU, aSpan, aPoles, aWeights, theValue, theDerivative); +} -bool BSplineImpl::isCacheValid() const +int BSplineImpl::spanIndex(double& u) { - // curve has to be initialized - bool isValid = myCurve.get() && !myCurve->isNull(); - - static const double THE_TOLERANCE = 1.e-7; - // compare poles - isValid = isValid && poles.size() == myCachedPoles.size(); - std::list::const_iterator aCachePIt = myCachedPoles.begin(); - GCS::VEC_P::const_iterator aPolesIt = poles.begin(); - for (; isValid && aPolesIt != poles.end(); ++aPolesIt, ++aCachePIt) { - isValid = isValid && fabs((*aCachePIt)->x() - *aPolesIt->x) < THE_TOLERANCE - && fabs((*aCachePIt)->y() - *aPolesIt->y) < THE_TOLERANCE; + if (myFlatKnots.empty()) { + // fill flat knots indices + for (int i = 0; i < (int)mult.size(); ++i) + myFlatKnots.resize(myFlatKnots.size() + mult[i], *knots[i]); + if (periodic) { + // additional knots at the beginning and the end to complete periodity + int anExtraBegin = degree + 1 - mult.front(); + int anExtraEnd = degree + 1 - mult.back(); + double aPeriod = *knots.back() - *knots.front(); + VEC_D aNewFlatKnots; + aNewFlatKnots.reserve(myFlatKnots.size() + (size_t)(anExtraBegin + anExtraEnd)); + auto it = myFlatKnots.end() - mult.back() - anExtraBegin; + while (anExtraBegin > 0) { + aNewFlatKnots.push_back(*(it++) - aPeriod); + --anExtraBegin; + } + aNewFlatKnots.insert(aNewFlatKnots.end(), myFlatKnots.begin(), myFlatKnots.end()); + it = myFlatKnots.begin() + mult.front(); + while (anExtraEnd > 0) { + aNewFlatKnots.push_back(*(it++) + aPeriod); + --anExtraEnd; + } + myFlatKnots = aNewFlatKnots; + } } - // compare weights - isValid = isValid && weights.size() == myCachedWeights.size(); - std::list::const_iterator aCacheWIt = myCachedWeights.begin(); - GCS::VEC_pD::const_iterator aWeightsIt = weights.begin(); - for (; isValid && aWeightsIt != weights.end(); ++aWeightsIt, ++aCacheWIt) - isValid = isValid && fabs(*aCacheWIt - **aWeightsIt) < THE_TOLERANCE; + if (periodic) + periodicNormalization(u, *knots.front(), *knots.back()); + + int anIndex = 0; + for (int i = 1; i < (int)knots.size() - 1; ++i) { + if (u <= *knots[i]) + break; + anIndex += mult[i]; + } + return anIndex; +} - return isValid; +void BSplineImpl::spanPolesAndWeights(int theSpanIndex, + double* theDerivParam, + std::vector& thePoles, + std::vector& theWeights) const +{ + thePoles.reserve(degree + 1); + theWeights.reserve(degree + 1); + for (int i = theSpanIndex; i <= theSpanIndex + degree; ++i) { + // optimization: weighted pole + int idx = i % (int)poles.size(); + thePoles.push_back(GCS::DeriVector2(poles[idx], theDerivParam).mult(*weights[idx])); + theWeights.push_back(*weights[idx]); + } +} + +void BSplineImpl::performDeBoor(double theU, + int theSpanIndex, + std::vector& thePoles, + std::vector& theWeights, + GCS::DeriVector2& theValue, + GCS::DeriVector2& theDerivative) const +{ + std::vector aPDeriv(thePoles.size(), DeriVector2()); + std::vector aWDeriv(theWeights.size(), 0.0); + for (int i = 0; i < degree; ++i) { + for (int j = degree; j > i; --j) { + double denom = (myFlatKnots[theSpanIndex + j + degree - i] - + myFlatKnots[theSpanIndex + j]); + double a = (theU - myFlatKnots[theSpanIndex + j]) / denom; + aPDeriv[j] = aPDeriv[j].linCombi(a, aPDeriv[j - 1], 1.0 - a).sum( + thePoles[j].subtr(thePoles[j - 1]).mult(1.0 / denom)); + aWDeriv[j] = aWDeriv[j] * a + aWDeriv[j - 1] * (1.0 - a) + + (theWeights[j] - theWeights[j - 1]) / denom; + thePoles[j] = thePoles[j].linCombi(a, thePoles[j - 1], 1.0 - a); + theWeights[j] = theWeights[j] * a + theWeights[j - 1] * (1.0 - a); + } + } + double w = 1 / theWeights[degree]; + theValue = thePoles[degree].mult(w); + theDerivative = aPDeriv[degree].subtr(theValue.mult(aWDeriv[degree])).mult(w); } -void BSplineImpl::rebuildCache() +bool BSplineImpl::parameter(const Point& thePoint, double& theParam) const { - myCachedPoles.clear(); - myCachedWeights.clear(); - myCachedKnots.clear(); - myCachedMultiplicities.clear(); - - for (GCS::VEC_P::iterator anIt = poles.begin(); anIt != poles.end(); ++anIt) - myCachedPoles.push_back(GeomPnt2dPtr(new GeomAPI_Pnt2d(*anIt->x, *anIt->y))); - for (GCS::VEC_pD::iterator anIt = weights.begin(); anIt != weights.end(); ++anIt) - myCachedWeights.push_back(**anIt); - for (GCS::VEC_pD::iterator anIt = knots.begin(); anIt != knots.end(); ++anIt) - myCachedKnots.push_back(**anIt); - myCachedMultiplicities.assign(mult.begin(), mult.end()); - - myCurve.reset(new GeomAPI_BSpline2d(degree, myCachedPoles, myCachedWeights, - myCachedKnots, myCachedMultiplicities, periodic)); + std::list aPoles; + std::list aWeights; + std::list aKnots; + std::list aMults; + + for (GCS::VEC_P::const_iterator anIt = poles.begin(); anIt != poles.end(); ++anIt) + aPoles.push_back(GeomPnt2dPtr(new GeomAPI_Pnt2d(*anIt->x, *anIt->y))); + for (GCS::VEC_pD::const_iterator anIt = weights.begin(); anIt != weights.end(); ++anIt) + aWeights.push_back(**anIt); + for (GCS::VEC_pD::const_iterator anIt = knots.begin(); anIt != knots.end(); ++anIt) + aKnots.push_back(**anIt); + aMults.assign(mult.begin(), mult.end()); + + std::shared_ptr aCurve( + new GeomAPI_BSpline2d(degree, aPoles, aWeights, aKnots, aMults, periodic)); + + GeomPnt2dPtr aPoint(new GeomAPI_Pnt2d(*thePoint.x, *thePoint.y)); + return aCurve->parameter(aPoint, 1e100, theParam); } } // namespace GCS diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h index c83e1a9dd..3b734538f 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h @@ -22,12 +22,6 @@ #include -#include -#include - -class GeomAPI_BSpline2d; -class GeomAPI_Pnt2d; - namespace GCS { /// \brife SHAPER's implementation of B-spline curves in PlaneGCS solver class BSplineImpl : public BSpline @@ -39,17 +33,32 @@ namespace GCS { virtual BSplineImpl* Copy(); private: - /// Verify the cached curve satisfies to the parameters - bool isCacheValid() const; - /// Poles or weights are changed, cache curve has to be rebuilt - void rebuildCache(); + /// Return the index of start knot for the given parameter. + /// Parameter is updated accordingly, if the B-spline curve is periodic + /// and the parameter is out of period. + int spanIndex(double& u); + + /// Collect the list of poles and their weights affecting the given span + void spanPolesAndWeights(int theSpanIndex, + double* theDerivParam, + std::vector& thePoles, + std::vector& theWeights) const; + + /// Execute De Boor algorithm to calculate B-spline curve's value + void performDeBoor(double theU, int theSpanIndex, + std::vector& thePoles, std::vector& theWeights, + GCS::DeriVector2& theValue, GCS::DeriVector2& theDerivative) const; + + /// Calculate the value and the first derivative for the given parameter on B-spline + void d1(double theU, double* theDerivParam, + GCS::DeriVector2& theValue, GCS::DeriVector2& theDerivative); + + /// Find the parameter on B-spline corresponding to the given point + /// \return \c false if it is unable to calculate the parameter + bool parameter(const Point& thePoint, double& theParam) const; private: - std::shared_ptr myCurve; /// cached B-spline curve - std::list > myCachedPoles; /// cached B-spline poles - std::list myCachedWeights; /// cached B-spline weights - std::list myCachedKnots; /// cached B-spline knots - std::list myCachedMultiplicities; /// cached B-spline multiplicities + VEC_D myFlatKnots; /// indices of knots duplicated by multiplicity }; }