* 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
#include <GeomAPI_Vertex.h>
#include <GeomAPI_ShapeExplorer.h>
+#include <Approx_ParametrizationType.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
-#include <TopoDS.hxx>
-#include <TopoDS_Edge.hxx>
-#include <TopExp_Explorer.hxx>
-#include <TColgp_HArray1OfPnt.hxx>
+#include <BSplSLib.hxx>
+#include <Geom_BSplineCurve.hxx>
+#include <Geom_BSplineSurface.hxx>
#include <GeomAPI_Interpolate.hxx>
+#include <GeomAPI_PointsToBSplineSurface.hxx>
#include <gp_Pnt.hxx>
#include <Precision.hxx>
+#include <TColgp_Array2OfPnt.hxx>
+#include <TColgp_HArray1OfPnt.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Edge.hxx>
-static void reorder(Handle(TColgp_HArray1OfPnt)& thePoints);
-
-//=================================================================================================
GeomEdgePtr GeomAlgoAPI_CurveBuilder::edge(const std::list<GeomPointPtr>& 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<GeomPointPtr>::const_iterator anIt = thePoints.begin();
- for (int i = 1; anIt != thePoints.end(); anIt++, i++) {
- GeomPointPtr aPoint = *anIt;
- aPoints->SetValue(i, aPoint->impl<gp_Pnt>());
- }
+ std::list<GeomPointPtr> 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<GeomPointPtr>::const_iterator anIt = aPointsCopy.begin();
+ for (int i = 1; anIt != aPointsCopy.end(); anIt++, i++) {
+ GeomPointPtr aPoint = *anIt;
+ aPoints->SetValue(i, aPoint->impl<gp_Pnt>());
}
// Initialize interpolator
- GeomAPI_Interpolate anInterp(aPoints, theIsClosed, gp::Resolution());
+ GeomAPI_Interpolate anInterp(aPoints, thePeriodic, gp::Resolution());
// Assign tangents if defined
if (theStartTangent && theEndTangent) {
return aResultShape;
}
-//================ Auxiliary functions ========================================================
-void reorder(Handle(TColgp_HArray1OfPnt)& thePoints)
+GeomEdgePtr GeomAlgoAPI_CurveBuilder::approximate(const std::list<GeomPointPtr>& 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<GeomPointPtr>::const_iterator anIt = thePoints.begin();
+ for (int i = 1; anIt != thePoints.end(); anIt++, i++) {
+ const gp_Pnt& aPoint = (*anIt)->impl<gp_Pnt>();
+ 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<GeomPointPtr>& 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<GeomPointPtr>::iterator aPIt = thePoints.begin();
+ GeomPointPtr aPrevPnt = *aPIt;
+ for (; aPIt != thePoints.end(); ++aPIt) {
+ GeomPointPtr aPnt = *aPIt;
+ std::list<GeomPointPtr>::iterator aNextIt = aPIt;
+ std::list<GeomPointPtr>::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<GeomPointPtr>::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
/// \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<GeomPointPtr>& 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<GeomPointPtr>& 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<GeomPointPtr>& thePoints);
};
#endif
{
return data()->refattr(theID);
}
+ /// Returns the refattrlist attribute by the identifier
+ inline std::shared_ptr<ModelAPI_AttributeRefAttrList> refattrlist(const std::string& theID)
+ {
+ return data()->refattrlist(theID);
+ }
/// Returns the reference attribute by the identifier
inline std::shared_ptr<ModelAPI_AttributeReference> reference(const std::string& theID)
{
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");
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);
bool myHaveFrame;
bool myEnableOnCheck;
+ bool myAlwaysShowTitle;
};
#endif /* ModuleBase_WidgetOptionalBox_H_ */
%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
#include <SketchPlugin_ConstraintPerpendicular.h>
#include <SketchPlugin_ConstraintRadius.h>
#include <SketchPlugin_ConstraintRigid.h>
+#include <SketchPlugin_CurveFitting.h>
#include <SketchPlugin_Trim.h>
#include <SketchPlugin_Split.h>
#include <SketchPlugin_ConstraintTangent.h>
return aBSpline;
}
+//--------------------------------------------------------------------------------------
+static std::shared_ptr<SketchAPI_BSpline> buildInterpolation(
+ const CompositeFeaturePtr& theSketch,
+ const FeaturePtr& theCurveFittingFeature,
+ const std::list<ModelHighAPI_RefAttr>& 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_BSpline> SketchAPI_Sketch::addInterpolation(
+ const std::list<ModelHighAPI_RefAttr>& 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_BSpline> SketchAPI_Sketch::addApproximation(
+ const std::list<ModelHighAPI_RefAttr>& 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_Projection> SketchAPI_Sketch::addProjection(
const ModelHighAPI_Selection & theExternalFeature,
#include <SketchPlugin_Sketch.h>
#include <SketchPlugin_SketchEntity.h>
+#include <ModelHighAPI_Double.h>
#include <ModelHighAPI_Interface.h>
#include <ModelHighAPI_Macro.h>
#include <ModelHighAPI_Selection.h>
//--------------------------------------------------------------------------------------
class ModelAPI_CompositeFeature;
class ModelAPI_Object;
-class ModelHighAPI_Double;
class ModelHighAPI_Integer;
class ModelHighAPI_RefAttr;
class ModelHighAPI_Reference;
const std::list<ModelHighAPI_Integer>& multiplicities = std::list<ModelHighAPI_Integer>(),
const bool periodic = false);
+ /// Add interpolation feature
+ SKETCHAPI_EXPORT
+ std::shared_ptr<SketchAPI_BSpline> addInterpolation(
+ const std::list<ModelHighAPI_RefAttr>& points,
+ const bool periodic = false,
+ const bool closed = false);
+
+ /// Add approximation feature
+ SKETCHAPI_EXPORT
+ std::shared_ptr<SketchAPI_BSpline> addApproximation(
+ const std::list<ModelHighAPI_RefAttr>& 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<SketchAPI_Projection> addProjection(
SketchPlugin_ConstraintRigid.h
SketchPlugin_ConstraintTangent.h
SketchPlugin_ConstraintVertical.h
+ SketchPlugin_CurveFitting.h
SketchPlugin_Ellipse.h
SketchPlugin_EllipticArc.h
SketchPlugin_ExternalValidator.h
SketchPlugin_ConstraintRigid.cpp
SketchPlugin_ConstraintTangent.cpp
SketchPlugin_ConstraintVertical.cpp
+ SketchPlugin_CurveFitting.cpp
SketchPlugin_Ellipse.cpp
SketchPlugin_EllipticArc.cpp
SketchPlugin_ExternalValidator.cpp
--- /dev/null
+// 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 <SketchPlugin_CurveFitting.h>
+
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
+#include <SketchPlugin_ConstraintCoincidence.h>
+#include <SketchPlugin_MacroBSpline.h>
+#include <SketchPlugin_Point.h>
+#include <SketchPlugin_Tools.h>
+#include <SketchPlugin_Sketch.h>
+
+#include <ModelAPI_AttributeBoolean.h>
+#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_AttributeRefAttrList.h>
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Validator.h>
+
+#include <Events_InfoMessage.h>
+
+#include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <GeomAlgoAPI_CurveBuilder.h>
+#include <GeomAlgoAPI_EdgeBuilder.h>
+
+#include <GeomAPI_BSpline.h>
+
+static void convertTo3D(SketchPlugin_Sketch* theSketch,
+ const AttributeRefAttrListPtr& theAttribute,
+ bool theClosedButNotPeriodic,
+ std::list<GeomPointPtr>& 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<FeaturePtr> 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<GeomAPI_Curve>(myTransientResult);
+ std::shared_ptr<GeomAPI_BSpline> aBSpline = std::make_shared<GeomAPI_BSpline>(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<GeomPointPtr> aPoles = aBSpline->poles();
+ AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+ 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<double> 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<double> 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<int> 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<GeomDataAPI_Point2D>(
+ aBSplineFeature->attribute(SketchPlugin_BSpline::START_ID()));
+ aStartPoint->setValue(aPolesAttr->pnt(0));
+
+ AttributePoint2DPtr aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ 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<std::pair<ObjectPtr, AttributePtr> > aPointsList = aPointsAttr->list();
+ std::list<std::pair<ObjectPtr, AttributePtr> >::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<GeomPointPtr> aCoordinates;
+ convertTo3D(sketch(), aPointsAttr, !isPeriodic && isClosed, aCoordinates);
+
+ // to keep mapping between points and attributes
+ std::map<GeomPointPtr, std::pair<ObjectPtr, AttributePtr> > aMap;
+ std::list<std::pair<ObjectPtr, AttributePtr> > aPointsList = aPointsAttr->list();
+ bool isPointAdded = aCoordinates.size() != aPointsList.size();
+ std::list<GeomPointPtr>::iterator aCoordIt = aCoordinates.begin();
+ std::list<std::pair<ObjectPtr, AttributePtr> >::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<ObjectPtr, AttributePtr>& 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<GeomPointPtr>& thePoints)
+{
+ std::list<std::pair<ObjectPtr, AttributePtr> > 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<GeomDataAPI_Point2D>(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<GeomPointPtr> 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<GeomPointPtr> aCoordinates;
+ convertTo3D(theSketch, thePoints, !thePeriodic && theClosed, aCoordinates);
+
+ GeomEdgePtr aResult;
+ if (aCoordinates.size() > 1)
+ aResult = GeomAlgoAPI_CurveBuilder::approximate(aCoordinates, thePeriodic, thePrecision);
+ return aResult;
+}
--- /dev/null
+// 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 <SketchPlugin.h>
+#include <SketchPlugin_SketchEntity.h>
+
+#include <GeomAPI_IPresentable.h>
+
+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<GeomAPI_Edge> myTransientResult; ///< Interpolation curve
+};
+
+#endif
if (boolean(CONTROL_POLYGON_ID())->value()) {
std::list<FeaturePtr> aControlPoles;
- createControlPolygon(aBSpline, aControlPoles);
+ createControlPolygon(aBSpline, myIsPeriodic, aControlPoles);
constraintsForPoles(aControlPoles);
}
}
}
void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline,
+ bool thePeriodic,
std::list<FeaturePtr>& thePoles)
{
AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
// 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);
}
private:
FeaturePtr createBSplineFeature();
- void createControlPolygon(FeaturePtr theBSpline, std::list<FeaturePtr>& thePoles);
+ /// Create control polygon for the B-spline and returns the list of its poles
+ static void createControlPolygon(FeaturePtr theBSpline,
+ bool thePeriodic,
+ std::list<FeaturePtr>& thePoles);
+
+ /// Create additional coincidences if other features were selected while creating the B-spline
void constraintsForPoles(const std::list<FeaturePtr>& thePoles);
/// Create Point feature coincident with the B-spline pole
const int thePoleIndex1,
const int thePoleIndex2 = -1);
friend class SketchPlugin_BSplineBase;
+ friend class SketchPlugin_CurveFitting;
private:
std::list<double> myKnots;
};
-/**\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
{
#include <SketchPlugin_MacroEllipticArc.h>
#include <SketchPlugin_SketchDrawer.h>
#include <SketchPlugin_SketchCopy.h>
+#include <SketchPlugin_CurveFitting.h>
#include <SketcherPrs_Tools.h>
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);
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()) {
aMsg->setState(SketchPlugin_MacroEllipticArc::ID(), aHasSketchPlane);
aMsg->setState(SketchPlugin_ConstraintDistanceHorizontal::ID(), aHasSketchPlane);
aMsg->setState(SketchPlugin_ConstraintDistanceVertical::ID(), aHasSketchPlane);
+ aMsg->setState(SketchPlugin_CurveFitting::ID(), aHasSketchPlane);
aMsg->setState(SketchPlugin_Offset::ID(), aHasSketchPlane);
// SketchRectangle is a python feature, so its ID is passed just as a string
aMsg->setState("SketchRectangle", aHasSketchPlane);
#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"
#include <ModelAPI_AttributeDouble.h>
#include <ModelAPI_AttributeInteger.h>
#include <ModelAPI_AttributeRefAttr.h>
+#include <ModelAPI_AttributeRefAttrList.h>
#include <ModelAPI_AttributeRefList.h>
#include <ModelAPI_AttributeSelectionList.h>
#include <ModelAPI_AttributeString.h>
return true;
}
+
+bool SketchPlugin_CurveFittingValidator::isValid(const FeaturePtr& theFeature,
+ const std::list<std::string>& 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;
+}
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<ModelAPI_Feature>& theFeature,
+ const std::list<std::string>& theArguments,
+ Events_InfoMessage& theError) const;
+};
+
#endif
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="en_US">
+
+ <context>
+ <name>workshop</name>
+ <message>
+ <source>Curve fitting</source>
+ <translation>Curve fitting</translation>
+ </message>
+ </context>
+
<context>
<name>Sketch:Model_FeatureValidator</name>
<message>
<translation></translation>
</message>
</context>
+
+ <!-- SketchCurveFitting -->
+ <context>
+ <name>SketchCurveFitting</name>
+ <message>
+ <source>Curve fitting</source>
+ <translation>Curve fitting</translation>
+ </message>
+ <message>
+ <source>Create curve passing through the points</source>
+ <translation>Create curve passing through the points</translation>
+ </message>
+ <message>
+ <source>Error: Feature "%1" does not support action "%2".</source>
+ <translation>Error: Feature "%1" does not support action "%2".</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:SketchPlugin_CurveFittingValidator</name>
+ <message>
+ <source>Not enough points selected. Need at least %1 points.</source>
+ <translation>Not enough points selected. Need at least %1 points.</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:points</name>
+ <message>
+ <source>Points</source>
+ <translation>Points</translation>
+ </message>
+ <message>
+ <source>Select points for curve fitting</source>
+ <translation>Select points for curve fitting</translation>
+ </message>
+ <message>
+ <source>Attribute "%1" is not initialized.</source>
+ <translation>Select points for interpolation</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:periodic</name>
+ <message>
+ <source>Periodic</source>
+ <translation>Periodic</translation>
+ </message>
+ <message>
+ <source>Make curve periodic</source>
+ <translation>Make curve periodic</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:closed</name>
+ <message>
+ <source>Closed</source>
+ <translation>Closed</translation>
+ </message>
+ <message>
+ <source>Make curve closed, but not periodic</source>
+ <translation>Make curve closed, but not periodic</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:type</name>
+ <message>
+ <source>Interpolation</source>
+ <translation>Interpolation</translation>
+ </message>
+ <message>
+ <source>Approximation</source>
+ <translation>Approximation</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:precision</name>
+ <message>
+ <source>Precision</source>
+ <translation>Precision</translation>
+ </message>
+ <message>
+ <source>Maximal distance from selected points to the curve</source>
+ <translation>Maximal distance from selected points to the curve</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:need_control_poly</name>
+ <message>
+ <source>Create control polygon</source>
+ <translation>Create control polygon</translation>
+ </message>
+ <message>
+ <source>Specify if the control polygon should be created</source>
+ <translation>Specify if the control polygon should be created</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:reorder_points</name>
+ <message>
+ <source>Reorder points</source>
+ <translation>Reorder points</translation>
+ </message>
+ <message>
+ <source>Sort selected points to minimize the distance heighbors</source>
+ <translation>Sort selected points to minimize the distance heighbors</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:Auxiliary</name>
+ <message>
+ <source>Auxiliary</source>
+ <translation>Auxiliary</translation>
+ </message>
+ <message>
+ <source>Construction element</source>
+ <translation>Construction element</translation>
+ </message>
+ </context>
+
</TS>
<source>Horizontal Distance</source>
<translation>Distance horizontale</translation>
</message>
+ <message>
+ <source>Curve fitting</source>
+ <translation>Courbe d'ajustement</translation>
+ </message>
<message>
<source>Length</source>
<translation>Longueur</translation>
</message>
</context>
+ <!-- SketchCurveFitting-->
+ <context>
+ <name>SketchCurveFitting</name>
+ <message>
+ <source>Curve fitting</source>
+ <translation>Courbe d'ajustement</translation>
+ </message>
+ <message>
+ <source>Create curve passing through the points</source>
+ <translation>Créer une courbe passant par les points</translation>
+ </message>
+ <message>
+ <source>Error: Feature "%1" does not support action "%2".</source>
+ <translation>Erreur: la fonctionnalité "% 1" ne prend pas en charge l'action "% 2".</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:SketchPlugin_CurveFittingValidator</name>
+ <message>
+ <source>Not enough points selected. Need at least %1 points.</source>
+ <translation>Pas assez de points sélectionnés. Besoin d'au moins %1 points.</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:points</name>
+ <message>
+ <source>Points</source>
+ <translation>Points</translation>
+ </message>
+ <message>
+ <source>Select points for curve fitting</source>
+ <translation>Sélectionner des points pour l'ajustement de courbe</translation>
+ </message>
+ <message>
+ <source>Attribute "%1" is not initialized.</source>
+ <translation>Sélectionner des points pour l'ajustement de courbe</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:periodic</name>
+ <message>
+ <source>Periodic</source>
+ <translation>Périodique</translation>
+ </message>
+ <message>
+ <source>Make curve periodic</source>
+ <translation>Rendre la courbe périodique</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:closed</name>
+ <message>
+ <source>Closed</source>
+ <translation>Fermé</translation>
+ </message>
+ <message>
+ <source>Make curve closed, but not periodic</source>
+ <translation>Rendre la courbe fermée, mais pas périodique</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:type</name>
+ <message>
+ <source>Interpolation</source>
+ <translation>Interpolation</translation>
+ </message>
+ <message>
+ <source>Approximation</source>
+ <translation>Approximation</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:precision</name>
+ <message>
+ <source>Precision</source>
+ <translation>Précision</translation>
+ </message>
+ <message>
+ <source>Maximal distance from selected points to the curve</source>
+ <translation>Distance maximale entre les points sélectionnés et la courbe</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:need_control_poly</name>
+ <message>
+ <source>Create control polygon</source>
+ <translation>Créer un polygone de contrôle</translation>
+ </message>
+ <message>
+ <source>Specify if the control polygon should be created</source>
+ <translation>Précisez si le polygone de contrôle doit être créé</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:reorder_points</name>
+ <message>
+ <source>Reorder points</source>
+ <translation>Réorganiser les points</translation>
+ </message>
+ <message>
+ <source>Sort selected points to minimize the distance heighbors</source>
+ <translation>Trier les points sélectionnés pour minimiser la distance des voisins</translation>
+ </message>
+ </context>
+ <context>
+ <name>SketchCurveFitting:Auxiliary</name>
+ <message>
+ <source>Auxiliary</source>
+ <translation>Auxiliaire</translation>
+ </message>
+ <message>
+ <source>Construction element</source>
+ <translation>Élément de construction</translation>
+ </message>
+ </context>
+
</TS>
--- /dev/null
+
+ .. _tui_create_interpolation:
+
+Create interpolation curve
+==========================
+
+.. literalinclude:: examples/interpolation.py
+ :linenos:
+ :language: python
+
+:download:`Download this script <examples/interpolation.py>`
+
+
+ .. _tui_create_approximation:
+
+Create approximation curve
+==========================
+
+.. literalinclude:: examples/approximation.py
+ :linenos:
+ :language: python
+
+:download:`Download this script <examples/approximation.py>`
--- /dev/null
+.. |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.
--- /dev/null
+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()
--- /dev/null
+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()
SketchBSpline SketchMacroBSpline SketchMacroBSplinePeriodic SketchBSplinePeriodic
SketchRectangle
SketchProjection
+ SketchCurveFitting
SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintDistanceHorizontal SketchConstraintDistanceVertical
SketchConstraintParallel SketchConstraintPerpendicular
SketchConstraintRigid SketchConstraintHorizontal SketchConstraintVertical
obligatory="0"
change_visual_attributes="true"/>
</feature>
+
+ <!-- Curve fitting -->
+ <feature id="SketchCurveFitting"
+ title="Curve fitting"
+ tooltip="Create curve passing through the points"
+ icon="icons/Sketch/curvefitting.png"
+ helpfile="curveFittingFeature.html">
+ <sketch_multi_selector id="points"
+ label="Points"
+ tooltip="Select points for curve fitting"
+ shape_types="Vertices"
+ use_external="true"
+ greed="true">
+ </sketch_multi_selector>
+ <switch id="type">
+ <case id="interpolation_type" title="Interpolation"/>
+ <case id="approximation_type" title="Approximation">
+ <doublevalue_editor id="precision"
+ label="Precision"
+ tooltip="Maximal distance from selected points to the curve"
+ default="1.e-3"
+ min="1.e-7">
+ <validator id="GeomValidators_Positive" parameters="0"/>
+ </doublevalue_editor>
+ </case>
+ </switch>
+ <boolvalue id="need_control_poly"
+ label="Create control polygon"
+ default="true"
+ tooltip="Specify if the control polygon should be created"/>
+ <optionalbox id="periodic"
+ title="Periodic"
+ tooltip="Make curve periodic"
+ default="false"
+ has_frame="false"
+ enable_on_check="false"
+ show_title="true">
+ <boolvalue id="closed"
+ label="Closed"
+ default="false"
+ tooltip="Make curve closed, but not periodic"
+ obligatory="1"/>
+ </optionalbox>
+ <boolvalue id="Auxiliary"
+ label="Auxiliary"
+ default="false"
+ tooltip="Construction element"
+ obligatory="0"
+ change_visual_attributes="true"/>
+ <validator id="SketchPlugin_CurveFittingValidator"/>
+ <action id="reorder_points"
+ label="Reorder points"
+ tooltip="Sort selected points to minimize the distance heighbors"/>
+ </feature>
</group>
<group id="Segmentation">
#include <GeomAPI_BSpline2d.h>
#include <GeomAPI_Pnt2d.h>
-#include <GeomAPI_XY.h>
#include <cmath>
namespace GCS
{
-DeriVector2 BSplineImpl::Value(double u, double du, double* derivparam)
-{
- if (!isCacheValid())
- rebuildCache();
-
- std::shared_ptr<GeomAPI_Pnt2d> aValue;
- std::shared_ptr<GeomAPI_XY> aDeriv;
- myCurve->D1(u, aValue, aDeriv);
-
- // calculate the derivative on solver's parameter
- std::shared_ptr<GeomAPI_Pnt2d> aValueDeriv(new GeomAPI_Pnt2d(0.0, 0.0));
- bool hasParam = false;
- std::list<GeomPnt2dPtr> 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<GeomAPI_BSpline2d> 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<GeomAPI_Pnt2d> aValue;
- std::shared_ptr<GeomAPI_XY> 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()
return new BSplineImpl(*this);
}
+void BSplineImpl::d1(double theU,
+ double* theDerivParam,
+ GCS::DeriVector2& theValue,
+ GCS::DeriVector2& theDerivative)
+{
+ int aSpan = spanIndex(theU);
+ std::vector<GCS::DeriVector2> aPoles;
+ std::vector<double> 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<GeomPnt2dPtr>::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<double>::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<GCS::DeriVector2>& thePoles,
+ std::vector<double>& 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<GCS::DeriVector2>& thePoles,
+ std::vector<double>& theWeights,
+ GCS::DeriVector2& theValue,
+ GCS::DeriVector2& theDerivative) const
+{
+ std::vector<GCS::DeriVector2> aPDeriv(thePoles.size(), DeriVector2());
+ std::vector<double> 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<GeomPnt2dPtr> aPoles;
+ std::list<double> aWeights;
+ std::list<double> aKnots;
+ std::list<int> 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<GeomAPI_BSpline2d> 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
#include <PlaneGCSSolver_Defs.h>
-#include <list>
-#include <memory>
-
-class GeomAPI_BSpline2d;
-class GeomAPI_Pnt2d;
-
namespace GCS {
/// \brife SHAPER's implementation of B-spline curves in PlaneGCS solver
class BSplineImpl : public BSpline
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<GCS::DeriVector2>& thePoles,
+ std::vector<double>& theWeights) const;
+
+ /// Execute De Boor algorithm to calculate B-spline curve's value
+ void performDeBoor(double theU, int theSpanIndex,
+ std::vector<GCS::DeriVector2>& thePoles, std::vector<double>& 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<GeomAPI_BSpline2d> myCurve; /// cached B-spline curve
- std::list<std::shared_ptr<GeomAPI_Pnt2d> > myCachedPoles; /// cached B-spline poles
- std::list<double> myCachedWeights; /// cached B-spline weights
- std::list<double> myCachedKnots; /// cached B-spline knots
- std::list<int> myCachedMultiplicities; /// cached B-spline multiplicities
+ VEC_D myFlatKnots; /// indices of knots duplicated by multiplicity
};
}