]> SALOME platform Git repositories - modules/geom.git/commitdiff
Salome HOME
0022885: EDF 10392 HYDRO + GEOM: Spline of a polyline is not possible and returns...
authorvsr <vsr@opencascade.com>
Thu, 12 Mar 2015 14:10:26 +0000 (17:10 +0300)
committervsr <vsr@opencascade.com>
Thu, 12 Mar 2015 14:10:26 +0000 (17:10 +0300)
src/GEOMImpl/GEOMImpl_SplineDriver.cxx

index 23f22f195ce920ae542065bcaf1797e707fad290..4b15e32773baadc44b5c7c23891aa10ca3c51f30 100644 (file)
 
 #include <Standard_NullObject.hxx>
 
+// Below macro specifies how the closed point set is processed (issue 0022885).
+// See below for more information.
+// Currently solution 4 is chosen!
+#define BSPLINE_PROCESS_CLOSED_PNTSET 4
+
+namespace
+{
+  /*!
+    \brief Generate list of points from the list of (x,y,z) coordinates
+    \param coords list of values specifying (x y z) coordinates of points
+    \return list of points
+    \internal
+  */
+  Handle(TColgp_HArray1OfPnt) pointsFromCoords(Handle(TColStd_HArray1OfReal) coords)
+  {
+    Standard_Integer length = coords->Length() / 3;
+
+    Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, length);
+    
+    for (int i = 0; i < length; i ++) {
+      Standard_Real x = coords->Value( i*3+1 );
+      Standard_Real y = coords->Value( i*3+2 );
+      Standard_Real z = coords->Value( i*3+3 );
+      points->SetValue(i+1, gp_Pnt(x, y, z));
+    }
+
+    return points;
+  }
+
+  /*!
+    \brief Generate list of points from the sequence of input objects
+    \param coords list of objects as it is stored within the CAF tree
+    \return list of points
+    \internal
+  */
+  Handle(TColgp_HArray1OfPnt) pointsFromObjs(Handle(TColStd_HSequenceOfTransient) objects)
+  {
+    Standard_Integer length = objects->Length();
+
+    Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, length);
+    
+    for (int i = 1; i <= length; i ++) {
+      TopoDS_Shape shape = Handle(GEOM_Function)::DownCast(objects->Value(i))->GetValue();
+      if (shape.ShapeType() != TopAbs_VERTEX)
+        // error: only vertices are allowed in the input
+        Standard_ConstructionError::Raise("Input should contain only vertices");
+      points->SetValue(i, BRep_Tool::Pnt(TopoDS::Vertex(shape)));
+    }
+
+    return points;
+  }
+}
+
 //=======================================================================
 //function : GetID
 //purpose  :
@@ -91,173 +144,166 @@ Standard_Integer GEOMImpl_SplineDriver::Execute(TFunction_Logbook& log) const
 
     bool useCoords = aCI.GetConstructorType() == COORD_CONSTRUCTOR;
 
-    Handle(TColStd_HArray1OfReal) aCoordsArray; // parametric case
-    Handle(TColStd_HSequenceOfTransient) aPoints; // points case
-
-    int aLen = 0;
-    if (useCoords) {
-      aCoordsArray = aCI.GetCoordinates();
-      aLen = aCoordsArray->Length() / 3;
-    }
-    else {
-      aPoints = aCI.GetPoints();
-      aLen = aPoints->Length();
-    }
-
-    if (aLen < 2) return 0;
-
-    TColgp_Array1OfPnt points (1, (useCoords ? aLen : 1));
-    if (useCoords) {
-      int anArrayLength = aCoordsArray->Length();
-      for (int i = 0, j = 1; i <= (anArrayLength-3); i += 3) {
-        gp_Pnt aPnt = gp_Pnt(aCoordsArray->Value(i+1), aCoordsArray->Value(i+2), aCoordsArray->Value(i+3));
-        points.SetValue(j, aPnt);
-        j++;
-      }
-    }
-
-    int aRealLen = aLen;
-
-    if (aType == SPLINE_BEZIER && aCI.GetIsClosed()) {
-      TopoDS_Vertex aV1;
-      if (useCoords) {
-        aV1 = BRepBuilderAPI_MakeVertex(points.Value(1));
-      }
-      else {
-        Handle(GEOM_Function) aFPoint = Handle(GEOM_Function)::DownCast(aPoints->Value(1));
-        TopoDS_Shape aFirstPnt = aFPoint->GetValue();
-        aV1 = TopoDS::Vertex(aFirstPnt);
-      }
-
-      TopoDS_Vertex aV2;
-      if (useCoords) {
-        aV2 = BRepBuilderAPI_MakeVertex(points.Value(aLen));
-      }
-      else {
-        Handle(GEOM_Function) aLPoint = Handle(GEOM_Function)::DownCast(aPoints->Value(aLen));
-        TopoDS_Shape aLastPnt = aLPoint->GetValue();
-        aV2 = TopoDS::Vertex(aLastPnt);
-      }
-
-      if (!aV1.IsNull() && !aV2.IsNull() && !aV1.IsSame(aV2)) {
-        aRealLen++;
-      }
-    }
-
-    int ind;
-    Standard_Boolean isSeveral = Standard_False;
-    gp_Pnt aPrevP;
-
-    TColgp_Array1OfPnt CurvePoints (1, aRealLen);
-    for (ind = 1; ind <= aLen; ind++) {
-      gp_Pnt aP;
-      if (useCoords) {
-        aP = points.Value(ind);
-        if (!isSeveral && ind > 1) {
-          if (aP.Distance(aPrevP) > Precision::Confusion()) {
-            isSeveral = Standard_True;
+    // collect points from input parameters: objects or coordinates
+    Handle(TColgp_HArray1OfPnt) points = useCoords ? pointsFromCoords(aCI.GetCoordinates()) : pointsFromObjs(aCI.GetPoints());
+    int length = points->Length();
+
+    if (length < 2) return 0; // error: not enough points in the input list
+
+    // reorder points if required (bspline only)
+    if ((aType == SPLINE_INTERPOLATION || aType == SPLINE_INTERPOL_TANGENTS) && aCI.GetDoReordering()) {
+      for (int i = 1; i < length - 1; i++) {
+        gp_Pnt pi = points->Value(i);
+        int nearest = 0;
+        double minDist = RealLast();
+        for (int j = i+1; j <= length; j++) {
+          double dist = pi.SquareDistance(points->Value(j));
+          if (dist < minDist && (minDist - dist) > Precision::Confusion()) {
+            nearest = j;
+            minDist = dist;
           }
         }
-        CurvePoints.SetValue(ind, aP);
-        aPrevP = aP;
-      }
-      else {
-        Handle(GEOM_Function) aRefPoint = Handle(GEOM_Function)::DownCast(aPoints->Value(ind));
-        TopoDS_Shape aShapePnt = aRefPoint->GetValue();
-        if (aShapePnt.ShapeType() == TopAbs_VERTEX) {
-          aP = BRep_Tool::Pnt(TopoDS::Vertex(aShapePnt));
-          if (!isSeveral && ind > 1) {
-            if (aP.Distance(aPrevP) > Precision::Confusion()) {
-              isSeveral = Standard_True;
-            }
-          }
-          CurvePoints.SetValue(ind, aP);
-          aPrevP = aP;
+        if (nearest > 0 && nearest != i + 1) {
+          // 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 p = points->Value(nearest);
+          for (int j = nearest; j > i+1; j--)
+            points->SetValue(j, points->Value(j-1));
+          points->SetValue(i+1, p);
         }
       }
-    }
+    } // end of reordering
+
+    bool closed = points->Value(1).Distance(points->Value(length)) <= gp::Resolution();
 
     if (aType == SPLINE_BEZIER) {
-      if (!isSeveral) {
-        Standard_ConstructionError::Raise("Points for Bezier Curve are too close");
+      // for Bezier curve we should append first point to the list if:
+      // a) "closed" flag is set, and
+      // b) first and last vertices are not too close
+      bool addFirst = aCI.GetIsClosed() && !closed;
+      
+      // re-fill points and check that there's enough points to create a curve
+      bool isValid = false;
+      TColgp_Array1OfPnt curvePoints(1, length + (addFirst ? 1 : 0));
+      gp_Pnt pp;
+      for (int i = 1; i <= length; i++) {
+        gp_Pnt p = points->Value(i);
+        if (!isValid && i > 1 && p.Distance(pp) > Precision::Confusion())
+          isValid = true;
+        curvePoints.SetValue(i, p);
+        pp = p;
       }
-      if (aRealLen > aLen) { // set last point equal to first for the closed curve
-        CurvePoints.SetValue(aRealLen, CurvePoints.Value(1));
-      }
-      Handle(Geom_BezierCurve) GBC = new Geom_BezierCurve (CurvePoints);
+
+      if (!isValid)
+        // error: not enough points to create curve
+        Standard_ConstructionError::Raise("Points for Bezier Curve are too close");
+      
+      // set last point equal to first for the closed Bezier curve
+      if (addFirst) curvePoints.SetValue(length+1, curvePoints.Value(1));
+      
+      // create Bezier curve
+      Handle(Geom_BezierCurve) GBC = new Geom_BezierCurve(curvePoints);
       aShape = BRepBuilderAPI_MakeEdge(GBC).Edge();
     }
     else {
-      //GeomAPI_PointsToBSpline GBC (CurvePoints);
-      //aShape = BRepBuilderAPI_MakeEdge(GBC).Edge();
-
-      if (aCI.GetDoReordering()) {
-        for (int curInd = 1; curInd < aLen - 1; curInd++) {
-          gp_Pnt curPnt = CurvePoints.Value(curInd);
-          int nearInd = 0;
-          double nearDist = RealLast();
-          for (ind = curInd + 1; ind <= aLen; ind++) {
-            double dist = curPnt.SquareDistance(CurvePoints.Value(ind));
-            if (dist < nearDist && (nearDist - dist) > Precision::Confusion()) {
-              nearInd = ind;
-              nearDist = dist;
-            }
-          }
-          if (nearInd > 0 && nearInd != curInd + 1) {
-            // Keep given order of points to use it in case of equidistant candidates
-            //               .-<---<-.
-            //              /         \
-            // o  o  o  c  o->o->o->o->n  o  o
-            //          |  |           |
-            //     curInd  curInd+1    nearInd
-            gp_Pnt nearPnt = CurvePoints.Value(nearInd);
-            for (ind = nearInd; ind > curInd + 1; ind--) {
-              CurvePoints.SetValue(ind, CurvePoints.Value(ind - 1));
-            }
-            CurvePoints.SetValue(curInd + 1, nearPnt);
-          }
-        }
+      // Below described processing of closed points set case
+      // is not done for constrained bsplined
+      bool typeok = aType == SPLINE_INTERPOLATION;
+#if BSPLINE_PROCESS_CLOSED_PNTSET == 1
+      // Last point is removed from the list if:
+      // a) first and last vertices are equal;
+      // b) "closed" flag is not taken into account.
+      // If first and last points are equal, we force "closed" flag to be set to true.
+      // For the case when first and last vertices are equal, this approach causes
+      // result different that would be if last point had NOT be removed and "closed" flag is false.
+      bool isClosed = typeok && (aCI.GetIsClosed() || closed);
+      bool removeLast = typeok && closed;
+      bool addFirst = false;
+#elif BSPLINE_PROCESS_CLOSED_PNTSET == 2
+      // Last point is removed from the list if:
+      // a) first and last vertices are equal;
+      // b) "closed" flag is set to true.
+      // Flag "closed" is taken "as is".
+      // For the case when first and last vertices are equal, this approach causes
+      // different results with "closed" flag set to true and false.
+      bool isClosed = typeok && aCI.GetIsClosed();
+      bool removeLast = typeok && aCI.GetIsClosed() && closed;
+      bool addFirst = false;
+#elif BSPLINE_PROCESS_CLOSED_PNTSET == 3
+      // Points are passed "as is" to the creator.
+      // If first and last points are equal, we force "closed" flag to be set to false.
+      // For the case when first and last vertices are equal, this approach gives
+      // the same results with "closed" flag set to true and false.
+      bool isClosed = typeok && aCI.GetIsClosed() && !closed;
+      bool removeLast = false;
+      bool addFirst = false;
+#elif BSPLINE_PROCESS_CLOSED_PNTSET == 4
+      // First point is added to the list if:
+      // a) first and last vertices are not equal;
+      // b) "closed" flag is set to true.
+      // In this case "closed" flag is forcidly set to false - bspline creator is 
+      // capable to create closed edge in this case.
+      // This approach gives the same results with "closed" flag set to true not
+      // depending on if set of points is closed or no.
+      // Also, it gives equal reqults in both case if set of points is closed or not
+      // and "closed" flag is set to true, in contrast to solution 3 above.
+      bool isClosed = false;
+      bool removeLast = false;
+      bool addFirst = typeok && aCI.GetIsClosed() && !closed;
+#else
+      // Points are passed "as is" to the creator.
+      // This causes an error when first point is equal to last one and
+      // "closed" flag is set to true; see bug 0022885.
+      bool isClosed = typeok && aCI.GetIsClosed();
+      bool removeLast = false;
+      bool addFirst = false;
+#endif
+
+      // remove last point or append first one if the conditions are observed (see above)
+      if (removeLast || addFirst) {
+        int extra = removeLast ? -1 : (addFirst ? 1 : 0 );
+        int nb = removeLast ? length-1 : length;
+        Handle(TColgp_HArray1OfPnt) curvePoints = new TColgp_HArray1OfPnt (1, length+extra);
+        for (int i = 1; i <= nb; i++)
+          curvePoints->SetValue(i, points->Value(i));
+        if (addFirst) curvePoints->SetValue(length+1, points->Value(1));
+        points = curvePoints;
       }
 
-      Handle(TColgp_HArray1OfPnt) aHCurvePoints = new TColgp_HArray1OfPnt (1, aLen);
-      for (ind = 1; ind <= aLen; ind++) {
-        aHCurvePoints->SetValue(ind, CurvePoints.Value(ind));
-      }
-
-      bool isClosed = Standard_False;
-      if (aType == SPLINE_INTERPOLATION)
-        isClosed = aCI.GetIsClosed();
-
-      GeomAPI_Interpolate GBC (aHCurvePoints, isClosed, gp::Resolution());
+      // initial set-up of curve creator
+      GeomAPI_Interpolate GBC(points, isClosed, gp::Resolution());
 
+      // add tangent vectors constraints
       if (aType == SPLINE_INTERPOL_TANGENTS) {
-        Handle(GEOM_Function) aVec1Ref  = aCI.GetFirstVector();
-        Handle(GEOM_Function) aVec2Ref  = aCI.GetLastVector();
+        Handle(GEOM_Function) aVec1Ref = aCI.GetFirstVector();
+        Handle(GEOM_Function) aVec2Ref = aCI.GetLastVector();
 
         if (aVec1Ref.IsNull() || aVec2Ref.IsNull())
+          // error: bad vector parameter is specified
           Standard_NullObject::Raise("Null object is given for a vector");
 
-        TopoDS_Shape aVec1Sh = aVec1Ref->GetValue();
-        TopoDS_Shape aVec2Sh = aVec2Ref->GetValue();
-
         // take orientation of edge into account to avoid regressions, as it was implemented so
-        gp_Vec aV1 = GEOMUtils::GetVector(aVec1Sh, Standard_True);
-        gp_Vec aV2 = GEOMUtils::GetVector(aVec2Sh, Standard_True);
+        gp_Vec aV1 = GEOMUtils::GetVector(aVec1Ref->GetValue(), Standard_True);
+        gp_Vec aV2 = GEOMUtils::GetVector(aVec2Ref->GetValue(), Standard_True);
 
+        // push constraint vectors to the curve creator
         GBC.Load(aV1, aV2, /*Scale*/Standard_True);
       }
 
+      // create bspline curve
       GBC.Perform();
       if (GBC.IsDone())
         aShape = BRepBuilderAPI_MakeEdge(GBC.Curve()).Edge();
-      else
-        return 0;
     }
   }
   else {
   }
 
-  if (aShape.IsNull()) return 0;
+  if (aShape.IsNull()) return 0; // error: bad result
 
   aFunction->SetValue(aShape);