CR23683: Geom_BSplineSurface incorrectly determines continuity for periodic cases
authorRoman Lygin <roman.lygin@gmail.com>
Wed, 17 Apr 2013 11:26:49 +0000 (15:26 +0400)
committerRoman Lygin <roman.lygin@gmail.com>
Wed, 17 Apr 2013 11:26:49 +0000 (15:26 +0400)
Add a checking into files Geom_OffsetSurface.cxx, Geom_OffsetCurve.cxx, Geom2d_OffsetCurve.cxx , which try to make continuity of bspline surfaces and curves more than C0 to build offset.
Delete duplicate code and checkings
Prevent change of offset curve / surface when exception is raised due to attempt to set new C0 basis curve / surface
Added new QA command OCC23683. Added test case bugs/moddata_3/bug23683

src/BSplCLib/BSplCLib.cdl
src/BSplCLib/BSplCLib.cxx
src/Geom/Geom_BSplineCurve.cxx
src/Geom/Geom_BSplineSurface.cxx
src/Geom/Geom_OffsetCurve.cxx
src/Geom/Geom_OffsetSurface.cxx
src/Geom2d/Geom2d_BSplineCurve.cxx
src/Geom2d/Geom2d_OffsetCurve.cxx
src/QABugs/QABugs_19.cxx
tests/bugs/moddata_3/bug23683 [new file with mode: 0644]

index c1b5340..616d774 100755 (executable)
@@ -104,7 +104,7 @@ package BSplCLib
     --  . Curves and Surfaces for Computer Aided Geometric Design,
     --    a practical guide Gerald Farin
 
-uses TColStd, gp, TColgp, math
+uses TColStd, gp, TColgp, math, GeomAbs
 
 
 is
@@ -353,6 +353,15 @@ is
     --  Analyses the distribution of multiplicities between
     --  the knot FromK1 and the Knot ToK2.
 
+    KnotAnalysis (Degree     : Integer;
+                  Periodic   : Boolean;
+                  CKnots     : Array1OfReal from TColStd;
+                  CMults     : Array1OfInteger from TColStd;
+                  KnotForm   : out BSplKnotDistribution from GeomAbs;
+                  MaxKnotMult: out Integer);
+    ---Purpose: Analyzes the array of knots.
+    --          Returns the form and the maximum knot multiplicity.
+
     Reparametrize (U1, U2 : Real;
                   Knots : in out Array1OfReal from TColStd);
     ---Purpose:
index 2af3c75..97053ae 100755 (executable)
@@ -650,6 +650,62 @@ BSplCLib_MultDistribution BSplCLib::MultForm
 }
 
 //=======================================================================
+//function : KnotAnalysis
+//purpose  : 
+//=======================================================================
+
+void BSplCLib::KnotAnalysis (const Standard_Integer         Degree,
+                             const Standard_Boolean         Periodic,
+                             const TColStd_Array1OfReal&    CKnots,
+                             const TColStd_Array1OfInteger& CMults,
+                             GeomAbs_BSplKnotDistribution&  KnotForm,
+                             Standard_Integer&              MaxKnotMult)
+{
+  KnotForm = GeomAbs_NonUniform;
+
+  BSplCLib_KnotDistribution KSet = 
+    BSplCLib::KnotForm (CKnots, 1, CKnots.Length());
+  
+
+  if (KSet == BSplCLib_Uniform) {
+    BSplCLib_MultDistribution MSet =
+      BSplCLib::MultForm (CMults, 1, CMults.Length());
+    switch (MSet) {
+    case BSplCLib_NonConstant   :       
+      break;
+    case BSplCLib_Constant      : 
+      if (CKnots.Length() == 2) {
+        KnotForm = GeomAbs_PiecewiseBezier;
+      }
+      else {
+        if (CMults (1) == 1)  KnotForm = GeomAbs_Uniform;   
+      }
+      break;
+    case BSplCLib_QuasiConstant :   
+      if (CMults (1) == Degree + 1) {
+        Standard_Real M = CMults (2);
+        if (M == Degree )   KnotForm = GeomAbs_PiecewiseBezier;
+        else if  (M == 1)   KnotForm = GeomAbs_QuasiUniform;
+      }
+      break;
+    }
+  }
+
+  Standard_Integer FirstKM = 
+    Periodic ? CKnots.Lower() :  BSplCLib::FirstUKnotIndex (Degree,CMults);
+  Standard_Integer LastKM =
+    Periodic ? CKnots.Upper() :  BSplCLib::LastUKnotIndex (Degree,CMults);
+  MaxKnotMult = 0;
+  if (LastKM - FirstKM != 1) {
+    Standard_Integer Multi;
+    for (Standard_Integer i = FirstKM + 1; i < LastKM; i++) {
+      Multi = CMults (i);
+      MaxKnotMult = Max (MaxKnotMult, Multi);
+    }
+  }
+}
+
+//=======================================================================
 //function : Reparametrize
 //purpose  : 
 //=======================================================================
index aba5daf..6643f79 100755 (executable)
@@ -75,63 +75,6 @@ static void CheckCurveData
 }
 
 //=======================================================================
-//function : KnotAnalysis
-//purpose  : Internal use only
-//=======================================================================
-
-static void KnotAnalysis
-(const Standard_Integer           Degree,
- const Standard_Boolean           Periodic,
- const TColStd_Array1OfReal&      CKnots,
- const TColStd_Array1OfInteger&   CMults,
- GeomAbs_BSplKnotDistribution&    KnotForm,
- Standard_Integer&                MaxKnotMult)
-{
-  KnotForm = GeomAbs_NonUniform;
-
-  BSplCLib_KnotDistribution KSet = 
-    BSplCLib::KnotForm (CKnots, 1, CKnots.Length());
-  
-
-  if (KSet == BSplCLib_Uniform) {
-    BSplCLib_MultDistribution MSet =
-      BSplCLib::MultForm (CMults, 1, CMults.Length());
-    switch (MSet) {
-    case BSplCLib_NonConstant   :       
-      break;
-    case BSplCLib_Constant      : 
-      if (CKnots.Length() == 2) {
-       KnotForm = GeomAbs_PiecewiseBezier;
-      }
-      else {
-       if (CMults (1) == 1)  KnotForm = GeomAbs_Uniform;   
-      }
-      break;
-    case BSplCLib_QuasiConstant :   
-      if (CMults (1) == Degree + 1) {
-       Standard_Real M = CMults (2);
-       if (M == Degree )   KnotForm = GeomAbs_PiecewiseBezier;
-       else if  (M == 1)   KnotForm = GeomAbs_QuasiUniform;
-      }
-      break;
-    }
-  }
-
-  Standard_Integer FirstKM = 
-    Periodic ? CKnots.Lower() :  BSplCLib::FirstUKnotIndex (Degree,CMults);
-  Standard_Integer LastKM = 
-    Periodic ? CKnots.Upper() :  BSplCLib::LastUKnotIndex (Degree,CMults);
-  MaxKnotMult = 0;
-  if (LastKM - FirstKM != 1) {
-    Standard_Integer Multi;
-    for (Standard_Integer i = FirstKM + 1; i < LastKM; i++) {
-      Multi = CMults (i);
-      MaxKnotMult = Max (MaxKnotMult, Multi);
-    }
-  }
-}
-
-//=======================================================================
 //function : Rational
 //purpose  : check rationality of an array of weights
 //=======================================================================
@@ -1181,7 +1124,7 @@ void Geom_BSplineCurve::UpdateKnots()
   rational = !weights.IsNull();
 
   Standard_Integer MaxKnotMult = 0;
-  KnotAnalysis (deg, 
+  BSplCLib::KnotAnalysis (deg, 
                periodic,
                knots->Array1(), 
                mults->Array1(), 
index 86f0f56..e390b7f 100755 (executable)
@@ -88,60 +88,6 @@ static void CheckSurfaceData
 }
 
 //=======================================================================
-//function : KnotAnalysis
-//purpose  : Internal use only.
-//=======================================================================
-
-static void KnotAnalysis
-(const Standard_Integer         Degree,
- const TColStd_Array1OfReal&    CKnots,
- const TColStd_Array1OfInteger& CMults,
- GeomAbs_BSplKnotDistribution&  KnotForm,
- Standard_Integer&              MaxKnotMult)
-{
-  KnotForm = GeomAbs_NonUniform;
-
-  BSplCLib_KnotDistribution KSet = 
-    BSplCLib::KnotForm (CKnots, 1, CKnots.Length());
-  
-
-  if (KSet == BSplCLib_Uniform) {
-    BSplCLib_MultDistribution MSet =
-      BSplCLib::MultForm (CMults, 1, CMults.Length());
-    switch (MSet) {
-    case BSplCLib_NonConstant   :       
-      break;
-    case BSplCLib_Constant      : 
-      if (CKnots.Length() == 2) {
-       KnotForm = GeomAbs_PiecewiseBezier;
-      }
-      else {
-       if (CMults (1) == 1)  KnotForm = GeomAbs_Uniform;   
-      }
-      break;
-    case BSplCLib_QuasiConstant :   
-      if (CMults (1) == Degree + 1) {
-       Standard_Real M = CMults (2);
-       if (M == Degree )   KnotForm = GeomAbs_PiecewiseBezier;
-       else if  (M == 1)   KnotForm = GeomAbs_QuasiUniform;
-      }
-      break;
-    }
-  }
-  
-  Standard_Integer FirstKM = BSplCLib::FirstUKnotIndex (Degree, CMults);
-  Standard_Integer LastKM  = BSplCLib::LastUKnotIndex  (Degree, CMults);
-  MaxKnotMult = 0;
-  if (LastKM - FirstKM != 1) {
-    Standard_Integer Multi;
-    for (Standard_Integer i = FirstKM + 1; i < LastKM; i++) {
-      Multi = CMults (i);
-      MaxKnotMult = Max (MaxKnotMult, Multi);
-    }
-  }
-}
-
-//=======================================================================
 //function : Rational
 //purpose  : Internal use only.
 //=======================================================================
@@ -1297,7 +1243,7 @@ void Geom_BSplineSurface::UpdateUKnots()
 {
 
   Standard_Integer MaxKnotMult = 0;
-  KnotAnalysis (udeg, 
+  BSplCLib::KnotAnalysis (udeg, uperiodic,
                uknots->Array1(), 
                umults->Array1(), 
                uknotSet, MaxKnotMult);
@@ -1337,7 +1283,7 @@ void Geom_BSplineSurface::UpdateUKnots()
 void Geom_BSplineSurface::UpdateVKnots()
 {
   Standard_Integer MaxKnotMult = 0;
-  KnotAnalysis (vdeg, 
+  BSplCLib::KnotAnalysis (vdeg, vperiodic,
                vknots->Array1(), 
                vmults->Array1(), 
                vknotSet, MaxKnotMult);
index b9b821e..cec8bb1 100755 (executable)
@@ -89,14 +89,12 @@ Handle(Geom_Geometry) Geom_OffsetCurve::Copy () const {
 Geom_OffsetCurve::Geom_OffsetCurve (const Handle(Curve)& C,
                                    const Standard_Real           Offset, 
                                    const Dir&           V      )
- : direction(V), offsetValue(Offset) {
-  
+ : direction(V), offsetValue(Offset)
+{
   if (C->DynamicType() == STANDARD_TYPE(Geom_OffsetCurve)) {
     Handle(OffsetCurve) OC = Handle(OffsetCurve)::DownCast(C);
-    if ((OC->BasisCurve())->Continuity() == GeomAbs_C0)  
-      Standard_ConstructionError::Raise();
+    SetBasisCurve (OC->BasisCurve());
 
-    basisCurve = Handle(Curve)::DownCast((OC->BasisCurve())->Copy());
     Standard_Real PrevOff = OC->Offset();
     gp_Vec V1(OC->Direction());
     gp_Vec V2(direction);
@@ -109,9 +107,9 @@ Geom_OffsetCurve::Geom_OffsetCurve (const Handle(Curve)& C,
       offsetValue = -Vdir.Magnitude();
       direction.SetXYZ((-Vdir).XYZ());
     }
-  } else {
-    if (C->Continuity() == GeomAbs_C0) Standard_ConstructionError::Raise();
-    basisCurve = Handle(Curve)::DownCast(C->Copy()); // DownCast: 10-03-93
+  }
+  else {
+    SetBasisCurve(C);
   }
 }
 
@@ -188,10 +186,36 @@ Standard_Real Geom_OffsetCurve::Period () const
 //purpose  : 
 //=======================================================================
 
-void Geom_OffsetCurve::SetBasisCurve (const Handle(Curve)& C) {
+void Geom_OffsetCurve::SetBasisCurve (const Handle(Curve)& C)
+{
+  Handle(Curve) aBasisCurve = Handle(Curve)::DownCast(C->Copy());
+
+  // Basis curve must be at least C1
+  if (aBasisCurve->Continuity() == GeomAbs_C0)
+  {
+    // For B-splines it is sometimes possible to increase continuity by removing 
+    // unnecessarily duplicated knots
+    if (aBasisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve)))
+    {
+      Handle(Geom_BSplineCurve) aBCurve = Handle(Geom_BSplineCurve)::DownCast(aBasisCurve);
+      Standard_Integer degree = aBCurve->Degree();
+      Standard_Real Toler = Precision::Confusion();
+      Standard_Integer start = aBCurve->IsPeriodic() ? 1 :  aBCurve->FirstUKnotIndex(),
+                       finish = aBCurve->IsPeriodic() ? aBCurve->NbKnots() :  aBCurve->LastUKnotIndex();
+      for (Standard_Integer i = start; i <= finish; i++)
+      {
+        Standard_Integer mult = aBCurve->Multiplicity(i);
+        if ( mult == degree )
+          aBCurve->RemoveKnot(i,degree - 1, Toler);
+      }
+    }
+
+    // Raise exception if still C0
+    if (aBasisCurve->Continuity() == GeomAbs_C0)
+      Standard_ConstructionError::Raise("Offset on C0 curve");
+  }
 
-  if (C->Continuity() == GeomAbs_C0) Standard_ConstructionError::Raise();
-  basisCurve = Handle(Curve)::DownCast(C->Copy()); // DownCast: 10-03-93
+  basisCurve = aBasisCurve;
 }
 
 
index fced0ab..097e143 100755 (executable)
@@ -229,19 +229,16 @@ Geom_OffsetSurface::Geom_OffsetSurface ( const Handle(Surface)& S,
                                         const Standard_Real             Offset ) 
  : offsetValue (Offset) 
 {
-  
   Handle(Geom_OffsetSurface) Off_S;
   Off_S = Handle(Geom_OffsetSurface)::DownCast(S);
   if (!Off_S.IsNull()) {
-     offsetValue += Off_S->Offset();
-     basisSurf   = Handle(Surface)::DownCast(Off_S->BasisSurface()->Copy());
-   }
+    offsetValue += Off_S->Offset();
+    SetBasisSurface (Off_S->BasisSurface());
+  }
   else {
-    basisSurf = Handle(Surface)::DownCast(S->Copy());
-    if (S->Continuity() == GeomAbs_C0) 
-      Standard_ConstructionError::Raise("Offset with no C1 Surface");
+    SetBasisSurface(S);
   }
-  equivSurf = Surface();
+
 //
 // Tolerance en dur pour l'instant ,mais on devrait la proposer dans le constructeur
 // et la mettre en champ, on pourrait utiliser par exemple pour l'extraction d'iso 
@@ -258,11 +255,45 @@ Geom_OffsetSurface::Geom_OffsetSurface ( const Handle(Surface)& S,
 //purpose  : 
 //=======================================================================
 
-void Geom_OffsetSurface::SetBasisSurface (const Handle(Surface)& S) {
+void Geom_OffsetSurface::SetBasisSurface (const Handle(Surface)& S)
+{
+  Handle(Surface) aBasisSurf = Handle(Surface)::DownCast(S->Copy());
+
+  // Basis surface must be at least C1
+  if (aBasisSurf->Continuity() == GeomAbs_C0)
+  {
+    // For B-splines it is sometimes possible to increase continuity by removing 
+    // unnecessarily duplicated knots
+    if (aBasisSurf->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))
+    {
+      Handle(Geom_BSplineSurface) aBSurf = Handle(Geom_BSplineSurface)::DownCast(aBasisSurf);
+      Standard_Integer uDegree = aBSurf->UDegree(), vDegree = aBSurf->VDegree();
+      Standard_Real Toler = Precision::Confusion();
+      Standard_Integer start = aBSurf->IsUPeriodic() ? 1 :  aBSurf->FirstUKnotIndex(),
+                       finish = aBSurf->IsUPeriodic() ? aBSurf->NbUKnots() :  aBSurf->LastUKnotIndex();
+      for (Standard_Integer i = start; i <= finish; i++)
+      {
+        Standard_Integer mult = aBSurf->UMultiplicity(i);
+        if ( mult == uDegree )
+          aBSurf->RemoveUKnot(i,uDegree - 1, Toler);
+      }
+      start = aBSurf->IsVPeriodic() ? 1 :  aBSurf->FirstVKnotIndex();
+      finish = aBSurf->IsVPeriodic() ? aBSurf->NbVKnots() :  aBSurf->LastVKnotIndex();
+      for (Standard_Integer i = start; i <= finish; i++)
+      {
+        Standard_Integer mult = aBSurf->VMultiplicity(i);
+        if ( mult == vDegree )
+          aBSurf->RemoveVKnot(i,vDegree - 1, Toler);
+      }
+    }
+
+    // Raise exception if still C0
+    if (aBasisSurf->Continuity() == GeomAbs_C0)
+      Standard_ConstructionError::Raise("Offset with no C1 Surface");
+  }
 
-  basisSurf = Handle(Surface)::DownCast(S->Copy());
+  basisSurf = aBasisSurf;
   equivSurf = Surface();
-  if (S->Continuity() == GeomAbs_C0)  Standard_ConstructionError::Raise();
 }
 
 
index 8fd69af..c696f3e 100755 (executable)
@@ -72,63 +72,6 @@ static void CheckCurveData
 }
 
 //=======================================================================
-//function : KnotAnalysis
-//purpose  : Internal use only
-//=======================================================================
-
-static void KnotAnalysis
-(const Standard_Integer           Degree,
- const Standard_Boolean           Periodic,
- const TColStd_Array1OfReal&      CKnots,
- const TColStd_Array1OfInteger&   CMults,
- GeomAbs_BSplKnotDistribution&    KnotForm,
- Standard_Integer&                MaxKnotMult)
-{
-  KnotForm = GeomAbs_NonUniform;
-
-  BSplCLib_KnotDistribution KSet = 
-    BSplCLib::KnotForm (CKnots, 1, CKnots.Length());
-  
-
-  if (KSet == BSplCLib_Uniform) {
-    BSplCLib_MultDistribution MSet =
-      BSplCLib::MultForm (CMults, 1, CMults.Length());
-    switch (MSet) {
-    case BSplCLib_NonConstant   :       
-      break;
-    case BSplCLib_Constant      : 
-      if (CKnots.Length() == 2) {
-       KnotForm = GeomAbs_PiecewiseBezier;
-      }
-      else {
-       if (CMults (1) == 1)  KnotForm = GeomAbs_Uniform;   
-      }
-      break;
-    case BSplCLib_QuasiConstant :   
-      if (CMults (1) == Degree + 1) {
-       Standard_Real M = CMults (2);
-       if (M == Degree )   KnotForm = GeomAbs_PiecewiseBezier;
-       else if  (M == 1)   KnotForm = GeomAbs_QuasiUniform;
-      }
-      break;
-    }
-  }
-
-  Standard_Integer FirstKM = 
-    Periodic ? CKnots.Lower() :  BSplCLib::FirstUKnotIndex (Degree,CMults);
-  Standard_Integer LastKM =
-    Periodic ? CKnots.Upper() :  BSplCLib::LastUKnotIndex (Degree,CMults);
-  MaxKnotMult = 0;
-  if (LastKM - FirstKM != 1) {
-    Standard_Integer Multi;
-    for (Standard_Integer i = FirstKM + 1; i < LastKM; i++) {
-      Multi = CMults (i);
-      MaxKnotMult = Max (MaxKnotMult, Multi);
-    }
-  }
-}
-
-//=======================================================================
 //function : Rational
 //purpose  : check rationality of an array of weights
 //=======================================================================
@@ -1298,7 +1241,7 @@ void Geom2d_BSplineCurve::UpdateKnots()
   rational = !weights.IsNull();
 
   Standard_Integer MaxKnotMult = 0;
-  KnotAnalysis (deg,
+  BSplCLib::KnotAnalysis (deg,
                periodic,
                knots->Array1(), 
                mults->Array1(), 
index dfbf545..583e88c 100755 (executable)
@@ -84,16 +84,11 @@ Geom2d_OffsetCurve::Geom2d_OffsetCurve (const Handle(Curve)& C,
 {
   if (C->DynamicType() == STANDARD_TYPE(Geom2d_OffsetCurve)) {
     Handle(OffsetCurve) OC = Handle(OffsetCurve)::DownCast(C);
-    if ((OC->BasisCurve())->Continuity() == GeomAbs_C0)  
-      Standard_ConstructionError::Raise();
-
-    basisCurve = Handle(Curve)::DownCast((OC->BasisCurve())->Copy());
+    SetBasisCurve (OC->BasisCurve());
     offsetValue += OC->Offset();
-  } else {
-    if (C->Continuity() == GeomAbs_C0)  
-      Standard_ConstructionError::Raise();
-
-    basisCurve = Handle(Curve)::DownCast(C->Copy());
+  }
+  else {
+    SetBasisCurve(C);
   }
 }
 
@@ -125,8 +120,34 @@ Standard_Real Geom2d_OffsetCurve::ReversedParameter( const Standard_Real U) cons
 
 void Geom2d_OffsetCurve::SetBasisCurve (const Handle(Curve)& C) 
 {
-  if (C->Continuity() == GeomAbs_C0)  Standard_ConstructionError::Raise();
-  basisCurve = Handle(Geom2d_Curve)::DownCast(C->Copy());
+  Handle(Geom2d_Curve) aBasisCurve = Handle(Geom2d_Curve)::DownCast(C->Copy());
+
+  // Basis curve must be at least C1
+  if (aBasisCurve->Continuity() == GeomAbs_C0)
+  {
+    // For B-splines it is sometimes possible to increase continuity by removing 
+    // unnecessarily duplicated knots
+    if (aBasisCurve->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)))
+    {
+      Handle(Geom2d_BSplineCurve) aBCurve = Handle(Geom2d_BSplineCurve)::DownCast(aBasisCurve);
+      Standard_Integer degree = aBCurve->Degree();
+      Standard_Real Toler = 1e-7;
+      Standard_Integer start = aBCurve->IsPeriodic() ? 1 :  aBCurve->FirstUKnotIndex(),
+                       finish = aBCurve->IsPeriodic() ? aBCurve->NbKnots() :  aBCurve->LastUKnotIndex();
+      for (Standard_Integer i = start; i <= finish; i++)
+      {
+        Standard_Integer mult = aBCurve->Multiplicity(i);
+        if ( mult == degree )
+          aBCurve->RemoveKnot(i,degree - 1, Toler);
+      }
+    }
+
+    // Raise exception if still C0
+    if (aBasisCurve->Continuity() == GeomAbs_C0)
+      Standard_ConstructionError::Raise("Offset on C0 curve");
+  }
+
+  basisCurve = aBasisCurve;
 }
 
 //=======================================================================
index 4b03646..36c214d 100755 (executable)
@@ -387,6 +387,26 @@ static Standard_Integer OCC23774(Draw_Interpretor& di, Standard_Integer n, const
  return 0;
 }
 
+static Standard_Integer OCC23683 (Draw_Interpretor& di, Standard_Integer argc,const char ** argv)
+{
+  if (argc < 2) {
+    di<<"Usage: " << argv[0] << " invalid number of arguments"<<"\n";
+    return 1;
+  }
+
+  Standard_Integer ucontinuity = 1;
+  Standard_Integer vcontinuity = 1;
+  Standard_Boolean iscnu = false;
+  Standard_Boolean iscnv = false;
+  
+  Handle(Geom_Surface) aSurf = DrawTrSurf::GetSurface(argv[1]);
+
+  QCOMPARE (aSurf->IsCNu (ucontinuity), iscnu);
+  QCOMPARE (aSurf->IsCNv (vcontinuity), iscnv);
+
+  return 0;
+}
+
 void QABugs::Commands_19(Draw_Interpretor& theCommands) {
   const char *group = "QABugs";
 
@@ -399,6 +419,7 @@ void QABugs::Commands_19(Draw_Interpretor& theCommands) {
   theCommands.Add ("OCC22611", "OCC22611 string nb", __FILE__, OCC22611, group);
   theCommands.Add ("OCC22595", "OCC22595", __FILE__, OCC22595, group);
   theCommands.Add ("OCC23774", "OCC23774 shape1 shape2", __FILE__, OCC23774, group);
+  theCommands.Add ("OCC23683", "OCC23683 shape", __FILE__, OCC23683, group);
 
   return;
 }
diff --git a/tests/bugs/moddata_3/bug23683 b/tests/bugs/moddata_3/bug23683
new file mode 100644 (file)
index 0000000..9c4c919
--- /dev/null
@@ -0,0 +1,20 @@
+puts "================"
+puts "OCC23683"
+puts "================"
+puts ""
+
+pload QAcommands
+
+restore [locate_data_file bug23683_a_31_draw] result
+set info [OCC23683 result]
+
+if { [regexp "iscnu: OK" $info] != 1 } {
+    puts "Error : ucontinuity is incorrect"
+}
+
+if { [regexp "iscnv: OK" $info] != 1 } {
+    puts "Error : vcontinuity is incorrect"
+}
+
+set 2dviewer 0
+