0023987: 2D fillets
authorvro <vro@opencascade.com>
Thu, 22 Aug 2013 09:04:16 +0000 (13:04 +0400)
committerbugmaster <bugmaster@opencascade.com>
Thu, 22 Aug 2013 09:40:53 +0000 (13:40 +0400)
Added test cases in group bugs/modalg_5
Test cases were put to corresponding regular grid fillet2d

25 files changed:
src/BRepTest/BRepTest_Fillet2DCommands.cxx
src/ChFi2d/ChFi2d.cdl
src/ChFi2d/ChFi2d_AnaFilletAlgo.cxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_AnaFilletAlgo.hxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_ChamferAPI.cxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_ChamferAPI.hxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_FilletAPI.cxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_FilletAPI.hxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_FilletAlgo.cxx [new file with mode: 0644]
src/ChFi2d/ChFi2d_FilletAlgo.hxx [new file with mode: 0644]
src/ChFi2d/FILES
tests/fillet2d/begin [new file with mode: 0644]
tests/fillet2d/chamfer2d/A1 [new file with mode: 0644]
tests/fillet2d/end [new file with mode: 0644]
tests/fillet2d/fillet2d/A1 [new file with mode: 0644]
tests/fillet2d/fillet2d/A2 [new file with mode: 0644]
tests/fillet2d/fillet2d/A3 [new file with mode: 0644]
tests/fillet2d/fillet2d/A4 [new file with mode: 0644]
tests/fillet2d/fillet2d/A5 [new file with mode: 0644]
tests/fillet2d/fillet2d/A6 [new file with mode: 0644]
tests/fillet2d/fillet2d/A7 [new file with mode: 0644]
tests/fillet2d/fillet2d/A8 [new file with mode: 0644]
tests/fillet2d/fillet2d/A9 [new file with mode: 0644]
tests/fillet2d/grids.list [new file with mode: 0644]
tests/fillet2d/parse.rules [new file with mode: 0644]

index ecc97c7..699997d 100755 (executable)
 #include <TopoDS_Shape.hxx>
 #include <TopoDS_Edge.hxx>
 #include <TopoDS_Vertex.hxx>
-#include <TopoDS.hxx>
+#include <TopExp_Explorer.hxx>
 #include <TopExp.hxx>
+#include <TopoDS.hxx>
+
+#include <ChFi2d_FilletAPI.hxx>
+#include <ChFi2d_ChamferAPI.hxx>
+
+#include <BRepBuilderAPI_MakeWire.hxx>
+#include <BRepBuilderAPI_FindPlane.hxx>
+#include <BRep_Builder.hxx>
 
 //=======================================================================
 //function : chfi2d
@@ -172,6 +180,258 @@ static Standard_Integer chfi2d(Draw_Interpretor& di, Standard_Integer n, const c
 }
 
 //=======================================================================
+//function : fillet2d
+//purpose  : A method to find a plane for 2 edges.
+//         : It may return a NULL object of the plane is not found
+//         : (the edge are located not in a plane).
+//=======================================================================
+
+static Handle(Geom_Plane) findPlane(const TopoDS_Shape& S)
+{
+  Handle(Geom_Plane) plane;
+  BRepBuilderAPI_FindPlane planeFinder(S);
+  if (planeFinder.Found())
+    plane = planeFinder.Plane();
+  return plane;
+}
+
+static Handle(Geom_Plane) findPlane(const TopoDS_Shape& E1, const TopoDS_Shape& E2)
+{
+  BRep_Builder B;
+  TopoDS_Compound C;
+  B.MakeCompound(C);
+  B.Add(C, E1);
+  B.Add(C, E2);
+  return findPlane(C);
+}
+
+//=======================================================================
+//function : findCommonPoint
+//purpose  : Find a common (or the most close) point of two edges.
+//=======================================================================
+
+static gp_Pnt findCommonPoint(const TopoDS_Shape& E1, const TopoDS_Shape& E2)
+{
+  TopoDS_Vertex v11, v12, v21, v22;
+  TopExp::Vertices(TopoDS::Edge(E1), v11, v12);
+  TopExp::Vertices(TopoDS::Edge(E2), v21, v22);
+
+  gp_Pnt p11 = BRep_Tool::Pnt(v11);
+  gp_Pnt p12 = BRep_Tool::Pnt(v12);
+  gp_Pnt p21 = BRep_Tool::Pnt(v21);
+  gp_Pnt p22 = BRep_Tool::Pnt(v22);
+
+  gp_Pnt common;
+  const double d1121 = p11.SquareDistance(p21);
+  const double d1122 = p11.SquareDistance(p22);
+  const double d1221 = p12.SquareDistance(p21);
+  const double d1222 = p12.SquareDistance(p22);
+  if (d1121 < d1122 && d1121 < d1221 && d1121 < d1222)
+    common = p11;
+  else if (d1122 < d1121 && d1122 < d1221 && d1122 < d1222)
+    common = p11;
+  else if (d1221 < d1121 && d1221 < d1122 && d1221 < d1222)
+    common = p12;
+  else if (d1222 < d1121 && d1222 < d1122 && d1222 < d1221)
+    common = p12;
+  
+  return common;
+}
+
+static gp_Pnt findCommonPoint(const TopoDS_Shape& W)
+{
+  // The common point for two edges inside a wire
+  // is a sharing vertex of two edges.
+  TopTools_MapOfShape vertices;
+  TopExp_Explorer expl(W, TopAbs_VERTEX);
+  for (; expl.More(); expl.Next())
+  {
+    if (!vertices.Add(expl.Current()))
+    {
+      return BRep_Tool::Pnt(TopoDS::Vertex(expl.Current()));
+    }
+  }
+  return gp::Origin(); // not found
+}
+
+//=======================================================================
+//function : fillet2d
+//purpose  : Fillet 2d based on Newton method (recursive, iteration)
+//usage    : fillet2d result wire (or edge1 edge2) radius
+//=======================================================================
+
+static Standard_Integer fillet2d(Draw_Interpretor& di, Standard_Integer n, const char** a)
+{
+  if (n != 4 && n != 5) 
+  {
+    di << "Usage : fillet2d result wire (or edge1 edge2) radius";
+    return 1;
+  }
+
+  TopoDS_Shape E1, E2, W;
+  if (n == 5)
+  {
+    // Get the edges.
+    E1 = DBRep::Get(a[2], TopAbs_EDGE, Standard_True);
+    E2 = DBRep::Get(a[3], TopAbs_EDGE, Standard_True);
+  }
+  else
+  {
+    // Get the wire.
+    W = DBRep::Get(a[2], TopAbs_WIRE, Standard_True);
+  }
+
+  // Get the radius value.
+  const Standard_Real radius = Atof(n == 5 ? a[4] : a[3]);
+
+  // Find plane of the edges.
+  Handle(Geom_Plane) hPlane = n == 5 ? findPlane(E1, E2) : findPlane(W);
+  if (hPlane.IsNull())
+  {
+    di << "Error: the edges are located not in a plane.";
+    return 1;
+  }
+
+  // Algo.
+  ChFi2d_FilletAPI algo;
+  gp_Pln plane = hPlane->Pln();
+  if (n == 5)
+  {
+    const TopoDS_Edge& e1 = TopoDS::Edge(E1);
+    const TopoDS_Edge& e2 = TopoDS::Edge(E2);
+    algo.Init(e1, e2, plane);
+  }
+  else
+  {
+    const TopoDS_Wire& w = TopoDS::Wire(W);
+    algo.Init(w, plane);
+  }
+  Standard_Boolean status = algo.Perform(radius);
+  if (!status)
+  {
+    di << "Error: the algrithm failed.";
+    return 1;
+  }
+
+  // Find a common point of the edges.
+  gp_Pnt common = n == 5 ? findCommonPoint(E1, E2) : findCommonPoint(W);
+
+  // Get the number of solutions (usually it is equal to 1).
+  Standard_Integer nbSolutions = algo.NbResults(common);
+  if (!nbSolutions)
+  {
+    di << "Error: no solutions.";
+    return 1;
+  }
+
+  // Get the result for the "nearest" solution (near the common point).
+  TopoDS_Edge M1, M2; // modified E1 and E2
+  TopoDS_Edge fillet = algo.Result(common, M1, M2);
+  if (fillet.IsNull())
+  {
+    di << "Error: the algrithm produced no result.";
+    return 1;
+  }
+
+  // Set result for DRAW.
+  DBRep::Set(a[1], fillet);
+
+  // Update neighbour edges in DRAW.
+  if (n == 5)
+  {
+    DBRep::Set(a[2], M1);
+    DBRep::Set(a[3], M2);
+  }
+  else // recreate the wire using the fillet
+  {
+    BRepBuilderAPI_MakeWire mkWire(M1, fillet, M2);
+    if (mkWire.IsDone())
+      DBRep::Set(a[1], mkWire.Wire());
+    else
+      DBRep::Set(a[1], fillet);
+  }
+  return 0;
+}
+
+//=======================================================================
+//function : chamfer2d
+//purpose  : Chamfer 2d.
+//usage    : chamfer2d result wire (or edge1 edge2) length1 length2
+//=======================================================================
+
+static Standard_Integer chamfer2d(Draw_Interpretor& di, Standard_Integer n, const char** a)
+{
+  if (n != 5 && n != 6) 
+  {
+    di << "Usage : chamfer2d result wire (or edge1 edge2) length1 length2";
+    return 1;
+  }
+
+  TopoDS_Shape W;
+  TopoDS_Shape E1, E2;
+  if (n == 6)
+  {
+    // Get the edges.
+    E1 = DBRep::Get(a[2], TopAbs_EDGE, Standard_True);
+    E2 = DBRep::Get(a[3], TopAbs_EDGE, Standard_True);
+  }
+  else 
+  {
+    W = DBRep::Get(a[2], TopAbs_WIRE, Standard_True);
+  }
+
+  // Get the lengths.
+  const Standard_Real length1 = (n == 6) ? Atof(a[4]) : Atof(a[3]);
+  const Standard_Real length2 = (n == 6) ? Atof(a[5]) : Atof(a[4]);
+
+  // Algo.
+  ChFi2d_ChamferAPI algo;
+  if (n == 6)
+  {
+    const TopoDS_Edge& e1 = TopoDS::Edge(E1);
+    const TopoDS_Edge& e2 = TopoDS::Edge(E2);
+    algo.Init(e1, e2);
+  }
+  else
+  {
+    const TopoDS_Wire& w = TopoDS::Wire(W);
+    algo.Init(w);
+  }
+
+  // Prepare the chamfer.
+  algo.Perform();
+
+  // Get the result.
+  TopoDS_Edge M1, M2; // modified E1 and E2
+  TopoDS_Edge chamfer = algo.Result(M1, M2, length1, length2);
+  if (chamfer.IsNull())
+  {
+    di << "Error: the algrithm produced no result.";
+    return 1;
+  }
+
+  if (n == 6)
+  {
+    // Set result for DRAW.
+    DBRep::Set(a[1], chamfer);
+    
+    // Update neighbour edges in DRAW.
+    DBRep::Set(a[2], M1);
+    DBRep::Set(a[3], M2);
+  }
+  else // recreate the wire using the chamfer
+  {
+    BRepBuilderAPI_MakeWire mkWire(M1, chamfer, M2);
+    if (mkWire.IsDone())
+      DBRep::Set(a[1], mkWire.Wire());
+    else
+      DBRep::Set(a[1], chamfer);
+  }
+
+  return 0;
+}
+
+//=======================================================================
 //function : Fillet2DCommands
 //purpose  : 
 //=======================================================================
@@ -187,4 +447,6 @@ void  BRepTest::Fillet2DCommands(Draw_Interpretor& theCommands)
   const char* g = "TOPOLOGY Fillet2D construction commands";
    
   theCommands.Add("chfi2d","chfi2d result face [edge1 edge2 (F radius/CDD d1 d2/CDA d ang) ....]",__FILE__,chfi2d,g);
+  theCommands.Add("fillet2d","fillet2d result wire (or edge1 edge2) radius",__FILE__,fillet2d,g);
+  theCommands.Add("chamfer2d","chamfer2d result wire (or edge1 edge2) length1 length2",__FILE__,chamfer2d,g);
 }
index e4b3d36..d749081 100755 (executable)
@@ -31,6 +31,23 @@ package ChFi2d
 
        ---Purpose: This package contains the algorithms used to build
        --          fillets or chamfers on planar wire.
+       --
+       -- This package provides two algorithms for 2D fillets:
+       --     ChFi2d_Builder - it constructs a fillet or chamfer 
+       --                      for linear and circular edges of a face.
+       --     ChFi2d_FilletAPI - it encapsulates two algorithms:
+       --         ChFi2d_AnaFilletAlgo - analytical constructor of the fillet.
+       --                                It works only for linear and circular edges,
+       --                                having a common point.
+       --         ChFi2d_FilletAlgo - iteration recursive method constructing 
+       --                             the fillet edge for any type of edges including
+       --                             ellipses and b-splines. 
+       --                             The edges may even have no common point.
+       --     ChFi2d_ChamferAPI - an algoroithm for construction of chamfers 
+       --                         between two linear edges of a plane.
+       --
+       -- The algorithms ChFi2d_AnaFilletAlgo and ChFi2d_FilletAlgo may be used directly 
+       -- or via the interface class ChFi2d_FilletAPI.
 
 uses
     TopoDS,
diff --git a/src/ChFi2d/ChFi2d_AnaFilletAlgo.cxx b/src/ChFi2d/ChFi2d_AnaFilletAlgo.cxx
new file mode 100644 (file)
index 0000000..8ff6235
--- /dev/null
@@ -0,0 +1,921 @@
+// Copyright (c) 1999-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#include <ChFi2d_AnaFilletAlgo.hxx>
+
+#include <gp_Ax3.hxx>
+#include <gp_Circ.hxx>
+#include <gp_Lin2d.hxx>
+#include <gp_Circ2d.hxx>
+
+#include <Standard_TypeMismatch.hxx>
+
+#include <BRepBuilderAPI_MakeEdge.hxx>
+#include <BRepBuilderAPI_MakeWire.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+
+#include <GeomAPI_ExtremaCurveCurve.hxx>
+#include <IntAna2d_AnaIntersection.hxx>
+#include <ShapeAnalysis_Wire.hxx>
+#include <Geom_Circle.hxx>
+
+#include <BRepAdaptor_Curve.hxx>
+#include <BRep_Tool.hxx>
+
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+
+#include <ProjLib.hxx>
+#include <TopExp.hxx>
+#include <ElSLib.hxx>
+
+// Compute the flag: CW || CCW
+static Standard_Boolean isCW(const BRepAdaptor_Curve& AC)
+{
+  const Standard_Real f = AC.FirstParameter();
+  const Standard_Real l = AC.LastParameter();
+  Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(AC.Curve().Curve());
+  gp_Pnt start = AC.Value(f);
+  gp_Pnt end = AC.Value(l);
+  gp_Pnt center = AC.Circle().Location();
+  gp_Ax3 plane = AC.Circle().Position();
+
+  // Get point on circle at half angle
+  gp_Pnt m;
+  circle->D0(0.5 * (f + l), m);
+
+  // Compare angles between vectors to middle point and to the end point.
+  gp_Vec startv(center, start), endv(center, end), middlev(center, m);
+  double middlea = startv.AngleWithRef(middlev, plane.Direction());
+  while(middlea < 0.0)
+    middlea += 2.0 * M_PI;
+  double enda = startv.AngleWithRef(endv, plane.Direction());
+  while(enda < 0.0)
+    enda += 2.0 * M_PI;
+
+  Standard_Boolean is_cw = middlea > enda ? Standard_True : Standard_False;
+  return is_cw;
+}
+
+// Equality of points computed through square distance between the points.
+static Standard_Boolean IsEqual(const gp_Pnt& p1, const gp_Pnt& p2)
+{
+  return p1.SquareDistance(p2) < Precision::SquareConfusion();
+}
+static Standard_Boolean IsEqual(const gp_Pnt2d& p1, const gp_Pnt2d& p2)
+{
+  return p1.SquareDistance(p2) < Precision::SquareConfusion();
+}
+
+// An empty constructor.
+// Use the method Init() to initialize the class.
+ChFi2d_AnaFilletAlgo::ChFi2d_AnaFilletAlgo()
+{
+
+}
+
+// An constructor.
+// It expects two edges having a common point of type:
+// - segment
+// - arc of circle.
+ChFi2d_AnaFilletAlgo::ChFi2d_AnaFilletAlgo(const TopoDS_Wire& theWire, 
+                                           const gp_Pln& thePlane)
+{
+  Init(theWire, thePlane);
+}
+
+// A constructor.
+// It expects two edges having a common point of type:
+// - segment
+// - arc of circle.
+ChFi2d_AnaFilletAlgo::ChFi2d_AnaFilletAlgo(const TopoDS_Edge& theEdge1, 
+                                           const TopoDS_Edge& theEdge2,
+                                           const gp_Pln& thePlane)
+{
+  // Make a wire consisting of two edges.
+  Init(theEdge1, theEdge2, thePlane);
+}
+
+// Initializes the class by a wire consisting of two edges.
+void ChFi2d_AnaFilletAlgo::Init(const TopoDS_Wire& theWire, const gp_Pln& thePlane)
+{
+  plane = thePlane;
+  TopoDS_Iterator itr(theWire);
+  for (; itr.More(); itr.Next())
+  {
+    if (e1.IsNull())
+      e1 = TopoDS::Edge(itr.Value());
+    else if (e2.IsNull())
+      e2 = TopoDS::Edge(itr.Value());
+  }
+  if (e1.IsNull() || e2.IsNull())
+    Standard_TypeMismatch::Raise("The algorithm expects a wire consisting of two linear or circular edges.");
+
+  // Left neighbour.
+  BRepAdaptor_Curve AC1(e1);
+  if (AC1.GetType() != GeomAbs_Line && AC1.GetType() != GeomAbs_Circle)
+    Standard_TypeMismatch::Raise("A segment or an arc of circle is expected.");
+
+  TopoDS_Vertex v1, v2;
+  TopExp::Vertices(e1, v1, v2, Standard_True);
+  if (v1.IsNull() || v2.IsNull())
+    Standard_Failure::Raise("An infinite edge.");
+
+  gp_Pnt P1 = BRep_Tool::Pnt(v1);
+  gp_Pnt P2 = BRep_Tool::Pnt(v2);
+  gp_Pnt2d p1 = ProjLib::Project(thePlane, P1);
+  gp_Pnt2d p2 = ProjLib::Project(thePlane, P2);
+  p1.Coord(x11, y11);
+  p2.Coord(x12, y12);
+
+  segment1 = true;
+  if (AC1.GetType() == GeomAbs_Circle)
+  {
+    segment1 = false;
+    gp_Circ c = AC1.Circle();
+
+    gp_Pnt2d loc = ProjLib::Project(thePlane, c.Location());
+    loc.Coord(xc1, yc1);
+
+    radius1 = c.Radius();
+    cw1 = isCW(AC1);
+  }
+
+  // Right neighbour.
+  BRepAdaptor_Curve AC2(e2);
+  if (AC2.GetType() != GeomAbs_Line && AC2.GetType() != GeomAbs_Circle)
+    Standard_TypeMismatch::Raise("A segment or an arc of circle is expected.");
+
+  TopExp::Vertices(e2, v1, v2, Standard_True);
+  if (v1.IsNull() || v2.IsNull())
+    Standard_Failure::Raise("An infinite edge.");
+
+  P1 = BRep_Tool::Pnt(v1);
+  P2 = BRep_Tool::Pnt(v2);
+  p1 = ProjLib::Project(thePlane, P1);
+  p2 = ProjLib::Project(thePlane, P2);
+  p1.Coord(x21, y21);
+  p2.Coord(x22, y22);
+
+  segment2 = true;
+  if (AC2.GetType() == GeomAbs_Circle)
+  {
+    segment2 = false;
+    gp_Circ c = AC2.Circle();
+
+    gp_Pnt2d loc = ProjLib::Project(thePlane, c.Location());
+    loc.Coord(xc2, yc2);
+
+    radius2 = c.Radius();
+    cw2 = isCW(AC2);
+  }
+}
+
+// Initializes the class by two edges.
+void ChFi2d_AnaFilletAlgo::Init(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2, 
+                                const gp_Pln& thePlane)
+{
+  // Make a wire consisting of two edges.
+
+  // Get common point.
+  TopoDS_Vertex v11, v12, v21, v22;
+  TopExp::Vertices(theEdge1, v11, v12, Standard_True);
+  TopExp::Vertices(theEdge2, v21, v22, Standard_True);
+  if (v11.IsNull() || v12.IsNull() || v21.IsNull() || v22.IsNull())
+    Standard_Failure::Raise("An infinite edge.");
+
+  gp_Pnt p11 = BRep_Tool::Pnt(v11);
+  gp_Pnt p12 = BRep_Tool::Pnt(v12);
+  gp_Pnt p21 = BRep_Tool::Pnt(v21);
+  gp_Pnt p22 = BRep_Tool::Pnt(v22);
+
+  gp_Pnt pcommon;
+  if (IsEqual(p11, p21) || IsEqual(p11, p22))
+  {
+    pcommon = p11;
+  }
+  else if (IsEqual(p12, p21) || IsEqual(p12, p22))
+  {
+    pcommon = p12;
+  }
+  else
+    Standard_Failure::Raise("The edges have no common point.");
+
+  // Reverse the edges in case of need (to construct a wire).
+  Standard_Boolean is1stReversed(Standard_False), is2ndReversed(Standard_False);
+  if (IsEqual(pcommon, p11))
+    is1stReversed = Standard_True;
+  else if (IsEqual(pcommon, p22))
+    is2ndReversed = Standard_True;
+
+  // Make a wire.
+  BRepBuilderAPI_MakeWire mkWire;
+  if (is1stReversed)
+    mkWire.Add(TopoDS::Edge(theEdge1.Reversed()));
+  else
+    mkWire.Add(theEdge1);
+  if (is2ndReversed)
+    mkWire.Add(TopoDS::Edge(theEdge2.Reversed()));
+  else
+    mkWire.Add(theEdge2);
+  if (!mkWire.IsDone())
+    Standard_Failure::Raise("Can't make a wire.");
+
+  const TopoDS_Wire& W = mkWire.Wire();
+  Init(W, thePlane);
+}
+
+// Calculates a fillet.
+Standard_Boolean ChFi2d_AnaFilletAlgo::Perform(const Standard_Real radius)
+{
+  Standard_Boolean bRet(false);
+  if (e1.IsNull() || e2.IsNull() ||
+      radius < Precision::Confusion())
+  {
+    return bRet;
+  }
+
+  // Fillet definition.
+  Standard_Real xc = 0.0, yc = 0.0;
+  Standard_Real start = 0.0, end = 0.0;             // parameters on neighbours
+  Standard_Real xstart = DBL_MAX, ystart = DBL_MAX; // point on left neighbour
+  Standard_Real xend = DBL_MAX, yend = DBL_MAX;     // point on right neighbour
+  Standard_Boolean cw = Standard_False;
+
+  // Analytical algorithm works for non-intersecting arcs only.
+  // Check arcs on self-intersection.
+  Standard_Boolean isCut(Standard_False);
+  if (!segment1 || !segment2)
+  {
+    BRepBuilderAPI_MakeWire mkWire(e1, e2);
+    if (mkWire.IsDone())
+    {
+      const TopoDS_Wire& W = mkWire.Wire();
+      BRepBuilderAPI_MakeFace mkFace(plane);
+      if (mkFace.IsDone())
+      {
+        const TopoDS_Face& F = mkFace.Face();
+        ShapeAnalysis_Wire analyzer(W, F, Precision::Confusion());
+        if (analyzer.CheckSelfIntersection() == Standard_True)
+        {
+          // Cut the edges at the point of intersection.
+          isCut = Standard_True;
+          if (!Cut(plane, e1, e2))
+          {
+            return Standard_False;
+          }
+        }
+      }
+    }
+  }// a case of segment - segment
+
+  // Choose the case.
+  BRepAdaptor_Curve AC1(e1), AC2(e2);
+  if (segment1 && segment2)
+  {
+    bRet = SegmentFilletSegment(radius, xc, yc, cw, start, end);
+  }
+  else if (segment1 && !segment2)
+  {
+    bRet = SegmentFilletArc(radius, xc, yc, cw, start, end, xend, yend);
+  }
+  else if (!segment1 && segment2)
+  {
+    bRet = ArcFilletSegment(radius, xc, yc, cw, start, end, xstart, ystart);
+  }
+  else if (!segment1 && !segment2)
+  {
+    bRet = ArcFilletArc(radius, xc, yc, cw, start, end);
+  }
+
+  if (!bRet)
+    return Standard_False;
+
+  // Invert the fillet for left-handed plane.
+  if (plane.Position().Direct() == Standard_False)
+    cw = !cw;
+
+  // Construct a fillet.
+  // Make circle.
+  gp_Pnt center = ElSLib::Value(xc, yc, plane);
+  const gp_Dir& normal = plane.Position().Direction();
+  gp_Circ circ(gp_Ax2(center, cw ? -normal : normal), radius);
+
+  // Fillet may only shrink a neighbour edge, it can't prolongate it.
+  const Standard_Real delta1 = AC1.LastParameter() - AC1.FirstParameter();
+  const Standard_Real delta2 = AC2.LastParameter() - AC2.FirstParameter();
+  if (!isCut && (start > delta1 || end > delta2))
+  {
+    // Check a case when a neighbour edge almost disappears: 
+    // try to reduce the fillet radius for a little (1.e-5 mm).
+    const Standard_Real little = 100.0 * Precision::Confusion();
+    const Standard_Real d1 = fabs(start - delta1);
+    const Standard_Real d2 = fabs(end   - delta2);
+    if (d1 < little || d2 < little)
+    {
+      if (segment1 && segment2)
+      {
+        bRet = SegmentFilletSegment(radius - little, xc, yc, cw, start, end);
+      }
+      else if (segment1 && !segment2)
+      {
+        bRet = SegmentFilletArc(radius - little, xc, yc, cw, start, end, xend, yend);
+      }
+      else if (!segment1 && segment2)
+      {
+        bRet = ArcFilletSegment(radius - little, xc, yc, cw, start, end, xstart, ystart);
+      }
+      else if (!segment1 && !segment2)
+      {
+        bRet = ArcFilletArc(radius - little, xc, yc, cw, start, end);
+      }
+      if (bRet)
+      {
+        // Invert the fillet for left-handed planes.
+        if (plane.Position().Direct() == Standard_False)
+          cw = !cw;
+
+        // Make the circle again.
+        center = ElSLib::Value(xc, yc, plane);
+        circ.SetLocation(center);
+        circ.SetRadius(radius - little);
+      }
+      else
+      {
+        return Standard_False;
+      }
+    }
+    else
+    {
+      return Standard_False;
+    }
+  }
+  if (bRet)
+  {
+    // start: (xstart, ystart) - pstart.
+    gp_Pnt pstart;
+    if (xstart != DBL_MAX)
+    {
+      pstart = ElSLib::Value(xstart, ystart, plane);
+    }
+    else
+    {
+      if (e1.Orientation() == TopAbs_FORWARD)
+        pstart = AC1.Value(AC1.LastParameter() - start);
+      else
+        pstart = AC1.Value(AC1.FirstParameter() + start);
+    }
+    // end: (xend, yend) -> pend.
+    gp_Pnt pend;
+    if (xend != DBL_MAX)
+    {
+      pend = ElSLib::Value(xend, yend, plane);
+    }
+    else
+    {
+      if (e2.Orientation() == TopAbs_FORWARD)
+        pend = AC2.Value(AC2.FirstParameter() + end);
+      else
+        pend = AC2.Value(AC2.LastParameter() - end);
+    }
+
+    // Make arc.
+    BRepBuilderAPI_MakeEdge mkEdge(circ, pstart, pend);
+    bRet = mkEdge.IsDone();
+    if (bRet)
+    {
+      fillet = mkEdge.Edge();
+
+      // Limit the neighbours.
+      // Left neighbour.
+      shrinke1.Nullify();
+      if (segment1)
+      {
+        BRepBuilderAPI_MakeEdge mkSegment1;
+        if (e1.Orientation() == TopAbs_FORWARD)
+          mkSegment1.Init(AC1.Curve().Curve(), AC1.FirstParameter(), AC1.LastParameter() - start);
+        else
+          mkSegment1.Init(AC1.Curve().Curve(), AC1.FirstParameter() + start, AC1.LastParameter());
+        if (mkSegment1.IsDone())
+          shrinke1 = mkSegment1.Edge();
+      }
+      else
+      {
+        BRepBuilderAPI_MakeEdge mkCirc1;
+        if (e1.Orientation() == TopAbs_FORWARD)
+          mkCirc1.Init(AC1.Curve().Curve(), AC1.FirstParameter(), AC1.LastParameter() - start);
+        else
+          mkCirc1.Init(AC1.Curve().Curve(), AC1.FirstParameter() + start, AC1.LastParameter());
+        if (mkCirc1.IsDone())
+          shrinke1 = mkCirc1.Edge();
+      }
+
+      // Right neighbour.
+      shrinke2.Nullify();
+      if (segment2)
+      {
+        BRepBuilderAPI_MakeEdge mkSegment2;
+        if (e2.Orientation() == TopAbs_FORWARD)
+          mkSegment2.Init(AC2.Curve().Curve(), AC2.FirstParameter() + end, AC2.LastParameter());
+        else
+          mkSegment2.Init(AC2.Curve().Curve(), AC2.FirstParameter(), AC2.LastParameter() - end);
+        if (mkSegment2.IsDone())
+          shrinke2 = mkSegment2.Edge();
+      }
+      else
+      {
+        BRepBuilderAPI_MakeEdge mkCirc2;
+        if (e2.Orientation() == TopAbs_FORWARD)
+          mkCirc2.Init(AC2.Curve().Curve(), AC2.FirstParameter() + end, AC2.LastParameter());
+        else
+          mkCirc2.Init(AC2.Curve().Curve(), AC2.FirstParameter(), AC2.LastParameter() - end);
+        if (mkCirc2.IsDone())
+          shrinke2 = mkCirc2.Edge();
+      }
+
+      bRet = !shrinke1.IsNull() && !shrinke2.IsNull();
+    }// fillet edge is done
+  }// shrinking is good
+
+  return bRet;
+}
+
+// Retrieves a result (fillet and shrinked neighbours).
+const TopoDS_Edge& ChFi2d_AnaFilletAlgo::Result(TopoDS_Edge& e1, TopoDS_Edge& e2)
+{
+  e1 = shrinke1;
+  e2 = shrinke2;
+  return fillet;
+}
+
+// WW5 method to compute fillet.
+// It returns a constructed fillet definition:
+//     center point (xc, yc)
+//     point on the 1st segment (xstart, ystart)
+//     point on the 2nd segment (xend, yend)
+//     is the arc of fillet clockwise (cw = true) or counterclockwise (cw = false).
+Standard_Boolean ChFi2d_AnaFilletAlgo::SegmentFilletSegment(const Standard_Real radius, 
+                                                            Standard_Real& xc, Standard_Real& yc, 
+                                                            Standard_Boolean& cw,
+                                                            Standard_Real& start, Standard_Real& end)
+{
+  // Make normalized vectors at p12.
+  gp_Pnt2d p11(x11, y11);
+  gp_Pnt2d p12(x12, y12);
+  gp_Pnt2d p22(x22, y22);
+
+  // Check length of segments.
+  if (IsEqual(p12, p11) || IsEqual(p12, p22))
+  {
+    return Standard_False;
+  }
+
+  // Make vectors.
+  gp_Vec2d v1(p12, p11);
+  gp_Vec2d v2(p12, p22);
+  v1.Normalize();
+  v2.Normalize();
+
+  // Make bisectrissa.
+  gp_Vec2d bisec = 0.5 * (v1 + v2);
+
+  // Check bisectrissa.
+  if (bisec.SquareMagnitude() < Precision::SquareConfusion())
+    return Standard_False;
+
+  // Normalize the bisectrissa.
+  bisec.Normalize();
+
+  // Angle at bisectrissa.
+  Standard_Real beta = v1.Angle(bisec);
+
+  // Length along the bisectrissa till the center of fillet.
+  Standard_Real L = radius / sin(fabs(beta));
+
+  // Center point of fillet.
+  gp_Pnt2d pc = p12.Translated(L * bisec);
+  pc.Coord(xc, yc);
+
+  // Shrinking length along segments.
+  start = sqrt(L * L - radius * radius);
+  end = start;
+
+  // Orientation of fillet.
+  cw = beta > 0.0;
+  return Standard_True;
+}
+
+// A function constructs a fillet between a segment and an arc.
+Standard_Boolean ChFi2d_AnaFilletAlgo::SegmentFilletArc(const Standard_Real radius, 
+                                                        Standard_Real& xc, Standard_Real& yc, 
+                                                        Standard_Boolean& cw,
+                                                        Standard_Real& start, Standard_Real& end, 
+                                                        Standard_Real& xend, Standard_Real& yend)
+{
+  // Make a line parallel to the segment at the side of center point of fillet.
+  // This side may be defined through making a bisectrissa for vectors at p12 (or p21).
+
+  // Make 2D points.
+  gp_Pnt2d p12(x12, y12);
+  gp_Pnt2d p11(x11, y11);
+  gp_Pnt2d pc2(xc2, yc2);
+
+  // Check length of segment.
+  if (p11.SquareDistance(p12) < gp::Resolution())
+    return Standard_False;
+
+  // Make 2D vectors.
+  gp_Vec2d v1(p12, p11);
+  gp_Vec2d v2(p12, pc2);
+
+  // Rotate the arc vector to become tangential at p21.
+  if (cw2)
+    v2.Rotate(+M_PI_2);
+  else
+    v2.Rotate(-M_PI_2);
+
+  // If vectors coincide (segment and arc are tangent),
+  // the algorithm doesn't work...
+  Standard_Real angle = v1.Angle(v2);
+  if (fabs(angle) < Precision::Angular())
+    return Standard_False;
+
+  // Make a bissectrisa of vectors at p12.
+  v2.Normalize();
+  v1.Normalize();
+  gp_Vec2d bisec = 0.5 * (v1 + v2);
+
+  // If segment and arc look in opposite direction, 
+  // no fillet is possible.
+  if (bisec.SquareMagnitude() < gp::Resolution())
+    return Standard_False;
+
+  // Define an appropriate point to choose center of fillet.
+  bisec.Normalize();
+  gp_Pnt2d nearp = p12.Translated(radius * bisec);
+  gp_Lin2d nearl(p12, bisec);
+
+  // Make a line parallel to segment and
+  // passing near the "near" point.
+  gp_Vec2d d1(v1);
+  gp_Lin2d line(p11, -d1);
+  d1.Rotate(M_PI_2);
+  line.Translate(radius * d1);
+  if (line.Distance(nearp) > radius)
+    line.Translate(-2.0 * radius * d1);
+
+  // Make a circle of radius of the arc +/- fillet radius.
+  gp_Ax2d axes(pc2, gp::DX2d());
+  gp_Circ2d circ(axes, radius2 + radius);
+  if (radius2 > radius && circ.Distance(nearp) > radius)
+    circ.SetRadius(radius2 - radius);
+
+  // Calculate intersection of the line and the circle.
+  IntAna2d_AnaIntersection intersector(line, circ);
+  if (!intersector.IsDone() || !intersector.NbPoints())
+    return Standard_False;
+
+  // Find center point of fillet.
+  Standard_Integer i;
+  Standard_Real minDist = DBL_MAX;
+  for (i = 1; i <= intersector.NbPoints(); ++i)
+  {
+    const IntAna2d_IntPoint& intp = intersector.Point(i);
+    const gp_Pnt2d& p = intp.Value();
+
+    Standard_Real d = nearl.Distance(p);
+    if (d < minDist)
+    {
+      minDist = d;
+      p.Coord(xc, yc);
+    }
+  }
+
+  // Shrink of segment.
+  gp_Pnt2d pc(xc, yc);
+  Standard_Real L2 = pc.SquareDistance(p12);
+  const Standard_Real Rf2 = radius * radius;
+  start = sqrt(L2 - Rf2);
+
+  // Shrink of arc.
+  gp_Vec2d pcc(pc2, pc);
+  end = fabs(gp_Vec2d(pc2, p12).Angle(pcc));
+
+  // Duplicate the information on shrink the arc:
+  // calculate a point on the arc coinciding with the end of fillet.
+  line.SetLocation(pc2);
+  line.SetDirection(pcc);
+  circ.SetLocation(pc2);
+  circ.SetRadius(radius2);
+  intersector.Perform(line, circ);
+  if (!intersector.IsDone() || !intersector.NbPoints())
+    return Standard_False;
+
+  xend = DBL_MAX;
+  yend = DBL_MAX;
+  for (i = 1; i <= intersector.NbPoints(); ++i)
+  {
+    const IntAna2d_IntPoint& intp = intersector.Point(i);
+    const gp_Pnt2d& p = intp.Value();
+
+    const Standard_Real d2 = p.SquareDistance(pc);
+    if (fabs(d2 - Rf2) < Precision::Confusion())
+    {
+      p.Coord(xend, yend);
+      break;
+    }
+  }
+
+  // Orientation of the fillet.
+  angle = v1.Angle(v2);
+  cw = angle > 0.0;
+  return Standard_True;
+}
+
+// A function constructs a fillet between an arc and a segment.
+Standard_Boolean ChFi2d_AnaFilletAlgo::ArcFilletSegment(const Standard_Real radius, 
+                                                        Standard_Real& xc, Standard_Real& yc, 
+                                                        Standard_Boolean& cw,
+                                                        Standard_Real& start, Standard_Real& end, 
+                                                        Standard_Real& xstart, Standard_Real& ystart)
+{
+  // Make a line parallel to the segment at the side of center point of fillet.
+  // This side may be defined through making a bisectrissa for vectors at p12 (or p21).
+
+  // Make 2D points.
+  gp_Pnt2d p12(x12, y12);
+  gp_Pnt2d p22(x22, y22);
+  gp_Pnt2d pc1(xc1, yc1);
+
+  // Check length of segment.
+  if (p12.SquareDistance(p22) < gp::Resolution())
+    return Standard_False;
+
+  // Make 2D vectors.
+  gp_Vec2d v1(p12, pc1);
+  gp_Vec2d v2(p12, p22);
+
+  // Rotate the arc vector to become tangential at p21.
+  if (cw1)
+    v1.Rotate(-M_PI_2);
+  else
+    v1.Rotate(+M_PI_2);
+
+  // If vectors coincide (segment and arc are tangent),
+  // the algorithm doesn't work...
+  Standard_Real angle = v1.Angle(v2);
+  if (fabs(angle) < Precision::Angular())
+    return Standard_False;
+
+  // Make a bisectrissa of vectors at p12.
+  v1.Normalize();
+  v2.Normalize();
+  gp_Vec2d bisec = 0.5 * (v1 + v2);
+
+  // If segment and arc look in opposite direction, 
+  // no fillet is possible.
+  if (bisec.SquareMagnitude() < gp::Resolution())
+    return Standard_False;
+
+  // Define an appropriate point to choose center of fillet.
+  bisec.Normalize();
+  gp_Pnt2d nearPoint = p12.Translated(radius * bisec);
+  gp_Lin2d nearLine(p12, bisec);
+
+  // Make a line parallel to segment and
+  // passing near the "near" point.
+  gp_Vec2d d2(v2);
+  gp_Lin2d line(p22, -d2);
+  d2.Rotate(M_PI_2);
+  line.Translate(radius * d2);
+  if (line.Distance(nearPoint) > radius)
+    line.Translate(-2.0 * radius * d2);
+
+  // Make a circle of radius of the arc +/- fillet radius.
+  gp_Ax2d axes(pc1, gp::DX2d());
+  gp_Circ2d circ(axes, radius1 + radius);
+  if (radius1 > radius && circ.Distance(nearPoint) > radius)
+    circ.SetRadius(radius1 - radius);
+
+  // Calculate intersection of the line and the big circle.
+  IntAna2d_AnaIntersection intersector(line, circ);
+  if (!intersector.IsDone() || !intersector.NbPoints())
+    return Standard_False;
+
+  // Find center point of fillet.
+  Standard_Integer i;
+  Standard_Real minDist = DBL_MAX;
+  for (i = 1; i <= intersector.NbPoints(); ++i)
+  {
+    const IntAna2d_IntPoint& intp = intersector.Point(i);
+    const gp_Pnt2d& p = intp.Value();
+
+    Standard_Real d = nearLine.Distance(p);
+    if (d < minDist)
+    {
+      minDist = d;
+      p.Coord(xc, yc);
+    }
+  }
+
+  // Shrink of segment.
+  gp_Pnt2d pc(xc, yc);
+  Standard_Real L2 = pc.SquareDistance(p12);
+  const Standard_Real Rf2 = radius * radius;
+  end = sqrt(L2 - Rf2);
+
+  // Shrink of arc.
+  gp_Vec2d pcc(pc1, pc);
+  start = fabs(gp_Vec2d(pc1, p12).Angle(pcc));
+
+  // Duplicate the information on shrink the arc:
+  // calculate a point on the arc coinciding with the start of fillet.
+  line.SetLocation(pc1);
+  line.SetDirection(pcc);
+  circ.SetLocation(pc1);
+  circ.SetRadius(radius1);
+  intersector.Perform(line, circ);
+  if (!intersector.IsDone() || !intersector.NbPoints())
+    return Standard_False;
+
+  xstart = DBL_MAX;
+  ystart = DBL_MAX;
+  for (i = 1; i <= intersector.NbPoints(); ++i)
+  {
+    const IntAna2d_IntPoint& intp = intersector.Point(i);
+    const gp_Pnt2d& p = intp.Value();
+
+    const Standard_Real d2 = p.SquareDistance(pc);
+    if (fabs(d2 - Rf2) < Precision::SquareConfusion())
+    {
+      p.Coord(xstart, ystart);
+      break;
+    }
+  }
+
+  // Orientation of the fillet.
+  angle = v2.Angle(v1);
+  cw = angle < 0.0;
+  return Standard_True;
+}
+
+// WW5 method to compute fillet: arc - arc.
+// It returns a constructed fillet definition:
+//     center point (xc, yc)
+//     shrinking parameter of the 1st circle (start)
+//     shrinking parameter of the 2nd circle (end)
+//     if the arc of fillet clockwise (cw = true) or counterclockwise (cw = false).
+Standard_Boolean ChFi2d_AnaFilletAlgo::ArcFilletArc(const Standard_Real radius, 
+                                                    Standard_Real& xc, Standard_Real& yc, 
+                                                    Standard_Boolean& cw,
+                                                    Standard_Real& start, Standard_Real& end)
+{
+  // Make points.
+  const gp_Pnt2d pc1(xc1, yc1);
+  const gp_Pnt2d pc2(xc2, yc2);
+  const gp_Pnt2d p12(x12, y12);
+
+  // Make vectors at p12.
+  gp_Vec2d v1(pc1, p12);
+  gp_Vec2d v2(pc2, p12);
+
+  // Rotate the vectors so that they are tangent to circles at p12.
+  if (cw1)
+    v1.Rotate(+M_PI_2);
+  else
+    v1.Rotate(-M_PI_2);
+  if (cw2)
+    v2.Rotate(-M_PI_2);
+  else
+    v2.Rotate(+M_PI_2);
+
+  // Make a "check" point for choosing an offset circle.
+  v1.Normalize();
+  v2.Normalize();
+  gp_Vec2d bisec = 0.5 * (v1 + v2);
+  if (bisec.SquareMagnitude() < gp::Resolution())
+    return Standard_False;
+
+  const gp_Pnt2d checkp = p12.Translated(radius * bisec);
+  const gp_Lin2d checkl(p12, bisec);
+
+  // Make two circles of radius r1 +/- r and r2 +/- r
+  // with center point equal to pc1 and pc2.
+  // Arc 1.
+  gp_Ax2d axes(pc1, gp::DX2d());
+  gp_Circ2d c1(axes, radius1 + radius);
+  if (radius1 > radius && c1.Distance(checkp) > radius)
+    c1.SetRadius(radius1 - radius);
+  // Arc 2.
+  axes.SetLocation(pc2);
+  gp_Circ2d c2(axes, radius2 + radius);
+  if (radius2 > radius && c2.Distance(checkp) > radius)
+    c2.SetRadius(radius2 - radius);
+
+  // Calculate an intersection point of these two circles
+  // and choose the one closer to the "check" point.
+  IntAna2d_AnaIntersection intersector(c1, c2);
+  if (!intersector.IsDone() || !intersector.NbPoints())
+    return Standard_False;
+
+  // Find center point of fillet.
+  gp_Pnt2d pc;
+  Standard_Real minDist = DBL_MAX;
+  for (int i = 1; i <= intersector.NbPoints(); ++i)
+  {
+    const IntAna2d_IntPoint& intp = intersector.Point(i);
+    const gp_Pnt2d& p = intp.Value();
+
+    Standard_Real d = checkp.SquareDistance(p);
+    if (d < minDist)
+    {
+      minDist = d;
+      pc = p;
+    }
+  }
+  pc.Coord(xc, yc);
+
+  // Orientation of fillet.
+  Standard_Real angle = v1.Angle(v2);
+  if (fabs(angle) < Precision::Angular())
+  {
+    angle = gp_Vec2d(pc, pc1).Angle(gp_Vec2d(pc, pc2));
+    cw = angle < 0.0;
+  }
+  else
+  {
+    cw = angle > 0.0;
+  }
+
+  // Shrinking of circles.
+  start = fabs(gp_Vec2d(pc1, p12).Angle(gp_Vec2d(pc1, pc)));
+  end = fabs(gp_Vec2d(pc2, p12).Angle(gp_Vec2d(pc2, pc)));
+  return Standard_True;
+}
+
+// Cuts intersecting edges of a contour.
+Standard_Boolean ChFi2d_AnaFilletAlgo::Cut(const gp_Pln& plane, TopoDS_Edge& e1, TopoDS_Edge& e2)
+{
+  gp_Pnt p;
+  Standard_Boolean found(Standard_False);
+  Standard_Real param1 = 0.0, param2 = 0.0;
+  Standard_Real f1, l1, f2, l2;
+  Handle(Geom_Curve) c1 = BRep_Tool::Curve(e1, f1, l1);
+  Handle(Geom_Curve) c2 = BRep_Tool::Curve(e2, f2, l2);
+  GeomAPI_ExtremaCurveCurve extrema(c1, c2, f1, l1, f2, l2);
+  if (extrema.NbExtrema())
+  {
+    Standard_Integer i, nb = extrema.NbExtrema();
+    for (i = 1; i <= nb; ++i)
+    {
+      const Standard_Real d = extrema.Distance(i);
+      if (d < Precision::Confusion())
+      {
+        extrema.Parameters(i, param1, param2);
+        if (fabs(l1 - param1) > Precision::Confusion() &&
+            fabs(f2 - param2) > Precision::Confusion())
+        {
+          found = Standard_True;
+          extrema.Points(i, p, p);
+          break;
+        }
+      }
+    }
+  }
+
+  if (found)
+  {
+    BRepBuilderAPI_MakeEdge mkEdge1(c1, f1, param1);
+    if (mkEdge1.IsDone())
+    {
+      e1 = mkEdge1.Edge();
+
+      BRepBuilderAPI_MakeEdge mkEdge2(c2, param2, l2);
+      if (mkEdge2.IsDone())
+      {
+        e2 = mkEdge2.Edge();
+
+        gp_Pnt2d p2d = ProjLib::Project(plane, p);
+        p2d.Coord(x12, y12);
+        x21 = x12;
+        y21 = y12;
+        return Standard_True;
+      }
+    }
+  }
+  return Standard_False;
+}
diff --git a/src/ChFi2d/ChFi2d_AnaFilletAlgo.hxx b/src/ChFi2d/ChFi2d_AnaFilletAlgo.hxx
new file mode 100644 (file)
index 0000000..5cc4abe
--- /dev/null
@@ -0,0 +1,139 @@
+// Created on: 2013-05-20
+// Created by: Vlad ROMASHKO
+// Copyright (c) 2003-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#ifndef _ANAFILLETALGO_H_
+#define _ANAFILLETALGO_H_
+
+#include <TopoDS_Wire.hxx>
+#include <TopoDS_Edge.hxx>
+#include <gp_Pln.hxx>
+
+//! An analytical algorithm for calculation of the fillets.
+//! It is implemented for segments and arcs of circle only.
+class ChFi2d_AnaFilletAlgo
+{
+public:
+
+  //! An empty constructor.
+  //! Use the method Init() to initialize the class.
+  Standard_EXPORT ChFi2d_AnaFilletAlgo();
+
+  //! A constructor.
+  //! It expects a wire consisting of two edges of type (any combination of):
+  //! - segment
+  //! - arc of circle.
+  Standard_EXPORT ChFi2d_AnaFilletAlgo(const TopoDS_Wire& theWire, 
+                                       const gp_Pln& thePlane);
+
+  //! A constructor.
+  //! It expects two edges having a common point of type:
+  //! - segment
+  //! - arc of circle.
+  Standard_EXPORT ChFi2d_AnaFilletAlgo(const TopoDS_Edge& theEdge1, 
+                                       const TopoDS_Edge& theEdge2,
+                                       const gp_Pln& thePlane);
+
+  //! Initializes the class by a wire consisting of two edges.
+  Standard_EXPORT void Init(const TopoDS_Wire& theWire, const gp_Pln& thePlane);
+
+  //! Initializes the class by two edges.
+  Standard_EXPORT void Init(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2, 
+                            const gp_Pln& thePlane);
+
+  //! Calculates a fillet.
+  Standard_EXPORT Standard_Boolean Perform(const Standard_Real radius);
+
+  //! Retrieves a result (fillet and shrinked neighbours).
+  Standard_EXPORT const TopoDS_Edge& Result(TopoDS_Edge& e1, TopoDS_Edge& e2);
+
+private:
+
+  // WW5 method to compute fillet.
+  // It returns a constructed fillet definition:
+  //     center point (xc, yc)
+  //     point on the 1st segment (xstart, ystart)
+  //     point on the 2nd segment (xend, yend)
+  //     is the arc of fillet clockwise (cw = true) or counterclockwise (cw = false).
+  Standard_Boolean SegmentFilletSegment(const Standard_Real radius, 
+                                        Standard_Real& xc, Standard_Real& yc, 
+                                        Standard_Boolean& cw,
+                                        Standard_Real& start, Standard_Real& end);
+
+  // A function constructs a fillet between a segment and an arc.
+  Standard_Boolean SegmentFilletArc(const Standard_Real radius, 
+                                    Standard_Real& xc, Standard_Real& yc, 
+                                    Standard_Boolean& cw,
+                                    Standard_Real& start, Standard_Real& end, 
+                                    Standard_Real& xend, Standard_Real& yend);
+
+  // A function constructs a fillet between an arc and a segment.
+  Standard_Boolean ArcFilletSegment(const Standard_Real radius, 
+                                    Standard_Real& xc, Standard_Real& yc, 
+                                    Standard_Boolean& cw,
+                                    Standard_Real& start, Standard_Real& end, 
+                                    Standard_Real& xstart, Standard_Real& ystart);
+
+  // WW5 method to compute fillet: arc - arc.
+  // It returns a constructed fillet definition:
+  //     center point (xc, yc)
+  //     shrinking parameter of the 1st circle (start)
+  //     shrinking parameter of the 2nd circle (end)
+  //     if the arc of fillet clockwise (cw = true) or counterclockwise (cw = false).
+  Standard_Boolean ArcFilletArc(const Standard_Real radius, 
+                                Standard_Real& xc, Standard_Real& yc, 
+                                Standard_Boolean& cw,
+                                Standard_Real& start, Standard_Real& end);
+
+  // Cuts intersecting edges of a contour.
+  Standard_Boolean Cut(const gp_Pln& plane, TopoDS_Edge& e1, TopoDS_Edge& e2);
+
+  // Plane.
+  gp_Pln           plane;
+
+  // Left neighbour.
+  TopoDS_Edge      e1;
+  Standard_Boolean segment1;
+  Standard_Real    x11;
+  Standard_Real    y11;
+  Standard_Real    x12;
+  Standard_Real    y12;
+  Standard_Real    xc1;
+  Standard_Real    yc1;
+  Standard_Real    radius1;
+  Standard_Boolean cw1;
+
+  // Right neighbour.
+  TopoDS_Edge      e2;
+  Standard_Boolean segment2;
+  Standard_Real    x21;
+  Standard_Real    y21;
+  Standard_Real    x22;
+  Standard_Real    y22;
+  Standard_Real    xc2;
+  Standard_Real    yc2;
+  Standard_Real    radius2;
+  Standard_Boolean cw2;
+
+  // Fillet (result).
+  TopoDS_Edge fillet;
+  TopoDS_Edge shrinke1;
+  TopoDS_Edge shrinke2;
+};
+
+#endif // _ANAFILLETALGO_H_
diff --git a/src/ChFi2d/ChFi2d_ChamferAPI.cxx b/src/ChFi2d/ChFi2d_ChamferAPI.cxx
new file mode 100644 (file)
index 0000000..38ad217
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright (c) 1999-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#include <ChFi2d_ChamferAPI.hxx>
+
+#include <Precision.hxx>
+#include <gp_Pnt.hxx>
+#include <GC_MakeLine.hxx>
+#include <BRep_Tool.hxx>
+#include <BRepBuilderAPI_MakeEdge.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopoDS.hxx>
+
+// An empty constructor.
+ChFi2d_ChamferAPI::ChFi2d_ChamferAPI()
+{
+
+}
+
+// A constructor accepting a wire consisting of two linear edges.
+ChFi2d_ChamferAPI::ChFi2d_ChamferAPI(const TopoDS_Wire& theWire)
+{
+  Init(theWire);
+}
+
+// A constructor accepting two linear edges.
+ChFi2d_ChamferAPI::ChFi2d_ChamferAPI(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2)
+{
+  Init(theEdge1, theEdge2);
+}
+
+// Initializes the class by a wire consisting of two libear edges.
+void ChFi2d_ChamferAPI::Init(const TopoDS_Wire& theWire)
+{
+  TopoDS_Edge E1, E2;
+  TopoDS_Iterator itr(theWire);
+  for (; itr.More(); itr.Next())
+  {
+    if (E1.IsNull())
+      E1 = TopoDS::Edge(itr.Value());
+    else if (E2.IsNull())
+      E2 = TopoDS::Edge(itr.Value());
+    else
+      break;
+  }
+  Init(E1, E2);
+}
+
+// Initializes the class by two linear edges.
+void ChFi2d_ChamferAPI::Init(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2)
+{
+  myEdge1 = theEdge1;
+  myEdge2 = theEdge2;
+}
+
+// Constructs a chamfer edge.
+// Returns true if the edge is constructed.
+Standard_Boolean ChFi2d_ChamferAPI::Perform()
+{
+  myCurve1 = BRep_Tool::Curve(myEdge1, myStart1, myEnd1);
+  myCurve2 = BRep_Tool::Curve(myEdge2, myStart2, myEnd2);
+  // searching for common points
+  if (myCurve1->Value(myStart1).IsEqual(myCurve2->Value(myEnd2), Precision::Confusion())) 
+  {
+    myCommonStart1 = true;
+    myCommonStart2 = false;
+  } 
+  else 
+  {
+    if (myCurve1->Value(myEnd1).IsEqual(myCurve2->Value(myStart2), Precision::Confusion())) 
+    {
+      myCommonStart1 = false;
+      myCommonStart2 = true;
+    } 
+    else
+    {
+      if (myCurve1->Value(myEnd1).IsEqual(myCurve2->Value(myEnd2), Precision::Confusion())) 
+      {
+        myCommonStart1 = false;
+        myCommonStart2 = false;
+      } 
+      else 
+      {
+        myCommonStart1 = true;
+        myCommonStart2 = true;
+      }
+    }
+  }
+  return Standard_True;
+}
+
+// Returns the result (chamfer edge, modified edge1, modified edge2).
+TopoDS_Edge ChFi2d_ChamferAPI::Result(TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2,
+                                      const Standard_Real theLength1, const Standard_Real theLength2) 
+{
+  TopoDS_Edge aResult;
+  if (Abs(myEnd1 - myStart1) < theLength1) 
+    return aResult;
+  if (Abs(myEnd2 - myStart2) < theLength2) 
+    return aResult;
+
+  Standard_Real aCommon1 = (myCommonStart1?myStart1:myEnd1) + (((myStart1 > myEnd1)^myCommonStart1)?theLength1:-theLength1);
+  Standard_Real aCommon2 = (myCommonStart2?myStart2:myEnd2) + (((myStart2 > myEnd2)^myCommonStart2)?theLength2:-theLength2);
+
+  // make chamfer edge
+  GC_MakeLine aML(myCurve1->Value(aCommon1), myCurve2->Value(aCommon2));
+  BRepBuilderAPI_MakeEdge aBuilder(aML.Value(), myCurve1->Value(aCommon1), myCurve2->Value(aCommon2));
+  aResult = aBuilder.Edge();
+  // divide first edge
+  BRepBuilderAPI_MakeEdge aDivider1(myCurve1, aCommon1, (myCommonStart1?myEnd1:myStart1));
+  theEdge1 = aDivider1.Edge();
+  // divide second edge
+  BRepBuilderAPI_MakeEdge aDivider2(myCurve2, aCommon2, (myCommonStart2?myEnd2:myStart2));
+  theEdge2 = aDivider2.Edge();
+
+  return aResult;
+}
diff --git a/src/ChFi2d/ChFi2d_ChamferAPI.hxx b/src/ChFi2d/ChFi2d_ChamferAPI.hxx
new file mode 100644 (file)
index 0000000..56f54dd
--- /dev/null
@@ -0,0 +1,63 @@
+// Created on: 2013-05-20
+// Created by: Mikhail PONIKAROV
+// Copyright (c) 2003-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#ifndef _CHAMFERAPI_H_
+#define _CHAMFERAPI_H_
+
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Wire.hxx>
+#include <Geom_Curve.hxx>
+
+//! A class making a chamfer between two linear edges.
+class ChFi2d_ChamferAPI
+{
+public:
+
+  //! An empty constructor.
+  Standard_EXPORT ChFi2d_ChamferAPI();
+
+  //! A constructor accepting a wire consisting of two linear edges.
+  Standard_EXPORT ChFi2d_ChamferAPI(const TopoDS_Wire& theWire);
+
+  //! A constructor accepting two linear edges.
+  Standard_EXPORT ChFi2d_ChamferAPI(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2);
+
+  //! Initializes the class by a wire consisting of two libear edges.
+  Standard_EXPORT void Init(const TopoDS_Wire& theWire);
+
+  //! Initializes the class by two linear edges.
+  Standard_EXPORT void Init(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2);
+
+  //! Constructs a chamfer edge.
+  //! Returns true if the edge is constructed.
+  Standard_EXPORT Standard_Boolean Perform();
+
+  // Returns the result (chamfer edge, modified edge1, modified edge2).
+  Standard_EXPORT TopoDS_Edge Result(TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2,
+                                     const Standard_Real theLength1, const Standard_Real theLength2);
+
+private:
+
+  TopoDS_Edge myEdge1, myEdge2;
+  Handle(Geom_Curve) myCurve1, myCurve2;
+  Standard_Real myStart1, myEnd1, myStart2, myEnd2;
+  Standard_Boolean myCommonStart1, myCommonStart2;
+};
+
+#endif // _CHAMFERAPI_H_
\ No newline at end of file
diff --git a/src/ChFi2d/ChFi2d_FilletAPI.cxx b/src/ChFi2d/ChFi2d_FilletAPI.cxx
new file mode 100644 (file)
index 0000000..f8962c7
--- /dev/null
@@ -0,0 +1,115 @@
+#include <ChFi2d_FilletAPI.hxx>
+#include <BRepAdaptor_Curve.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TopoDS.hxx>
+
+// An empty constructor of the fillet algorithm.
+// Call a method Init() to initialize the algorithm
+// before calling of a Perform() method.
+ChFi2d_FilletAPI::ChFi2d_FilletAPI():myIsAnalytical(Standard_False)
+{
+
+}
+
+// A constructor of a fillet algorithm: accepts a wire consisting of two edges in a plane.
+ChFi2d_FilletAPI::ChFi2d_FilletAPI(const TopoDS_Wire& theWire, 
+                                   const gp_Pln& thePlane):myIsAnalytical(Standard_False)
+{
+  Init(theWire, thePlane);
+}
+
+// A constructor of a fillet algorithm: accepts two edges in a plane.
+ChFi2d_FilletAPI::ChFi2d_FilletAPI(const TopoDS_Edge& theEdge1, 
+                                   const TopoDS_Edge& theEdge2, 
+                                   const gp_Pln& thePlane):myIsAnalytical(Standard_False)
+{
+  Init(theEdge1, theEdge2, thePlane);
+}
+
+// Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane.
+void ChFi2d_FilletAPI::Init(const TopoDS_Wire& theWire, 
+                            const gp_Pln& thePlane)
+{
+  // Decide whether we may apply an analytical solution.
+  TopoDS_Edge E1, E2;
+  TopoDS_Iterator itr(theWire);
+  for (; itr.More(); itr.Next())
+  {
+    if (E1.IsNull())
+      E1 = TopoDS::Edge(itr.Value());
+    else if (E2.IsNull())
+      E2 = TopoDS::Edge(itr.Value());
+    else
+      break;
+  }
+  if (!E1.IsNull() && !E2.IsNull())
+    myIsAnalytical = IsAnalytical(E1, E2);
+
+  // Initialize the algorithm.
+  myIsAnalytical ? myAnaFilletAlgo.Init(theWire, thePlane) :
+                   myFilletAlgo.Init(theWire, thePlane);
+}
+
+// Initializes a fillet algorithm: accepts two edges in a plane.
+void ChFi2d_FilletAPI::Init(const TopoDS_Edge& theEdge1, 
+                            const TopoDS_Edge& theEdge2, 
+                            const gp_Pln& thePlane)
+{
+  // Decide whether we may apply an analytical solution.
+  myIsAnalytical = IsAnalytical(theEdge1, theEdge2);
+
+  // Initialize the algorithm.
+  myIsAnalytical ? myAnaFilletAlgo.Init(theEdge1, theEdge2, thePlane) :
+                   myFilletAlgo.Init(theEdge1, theEdge2, thePlane);
+}
+
+// Returns true, if at least one result was found.
+Standard_Boolean ChFi2d_FilletAPI::Perform(const Standard_Real theRadius)
+{
+  return myIsAnalytical ? myAnaFilletAlgo.Perform(theRadius) : 
+                          myFilletAlgo.Perform(theRadius);
+}
+
+// Returns number of possible solutions.
+Standard_Integer ChFi2d_FilletAPI::NbResults(const gp_Pnt& thePoint)
+{
+  return myIsAnalytical ? 1 : myFilletAlgo.NbResults(thePoint);
+}
+
+// Returns result (fillet edge, modified edge1, modified edge2), 
+// nearest to the given point <thePoint> if iSolution == -1
+TopoDS_Edge ChFi2d_FilletAPI::Result(const gp_Pnt& thePoint, 
+                                     TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2, 
+                                     const Standard_Integer iSolution)
+{
+  return myIsAnalytical ? myAnaFilletAlgo.Result(theEdge1, theEdge2) :
+                          myFilletAlgo.Result(thePoint, theEdge1, theEdge2, iSolution);
+}
+
+// Decides whether the input parameters may use an analytical algorithm
+// for calculation of the fillets, or an iteration-recursive method is needed.
+// The analytical solution is applicable for linear and circular edges having a common point.
+Standard_Boolean ChFi2d_FilletAPI::IsAnalytical(const TopoDS_Edge& theEdge1, 
+                                                const TopoDS_Edge& theEdge2)
+{
+  Standard_Boolean ret(Standard_False);
+  BRepAdaptor_Curve AC1(theEdge1), AC2(theEdge2);
+  if ((AC1.GetType() == GeomAbs_Line || AC1.GetType() == GeomAbs_Circle) &&
+      (AC2.GetType() == GeomAbs_Line || AC2.GetType() == GeomAbs_Circle))
+  {
+    // The edges are lines or arcs of circle.
+    // Now check wether they have a common point.
+    gp_Pnt p11 = AC1.Value(AC1.FirstParameter());
+    gp_Pnt p12 = AC1.Value(AC1.LastParameter());
+    gp_Pnt p21 = AC2.Value(AC2.FirstParameter());
+    gp_Pnt p22 = AC2.Value(AC2.LastParameter());
+    if (p11.SquareDistance(p21) < Precision::SquareConfusion() ||
+        p11.SquareDistance(p22) < Precision::SquareConfusion() ||
+        p12.SquareDistance(p21) < Precision::SquareConfusion() ||
+        p12.SquareDistance(p22) < Precision::SquareConfusion())
+    {
+      ret = Standard_True;
+    }
+  }
+  return ret;
+}
diff --git a/src/ChFi2d/ChFi2d_FilletAPI.hxx b/src/ChFi2d/ChFi2d_FilletAPI.hxx
new file mode 100644 (file)
index 0000000..b5f6518
--- /dev/null
@@ -0,0 +1,103 @@
+// Created on: 2013-06-06
+// Created by: Vlad ROMASHKO
+// Copyright (c) 2003-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#ifndef _CHFI2D_FILLETAPI_H_
+#define _CHFI2D_FILLETAPI_H_
+
+#include <ChFi2d_FilletAlgo.hxx>
+#include <ChFi2d_AnaFilletAlgo.hxx>
+
+//! An interface class for 2D fillets.
+//! Open CASCADE provides two algorithms for 2D fillets:
+//!     ChFi2d_Builder - it constructs a fillet or chamfer 
+//!                      for linear and circular edges of a face.
+//!     ChFi2d_FilletAPI - it encapsulates two algorithms:
+//!         ChFi2d_AnaFilletAlgo - analytical constructor of the fillet.
+//!                                It works only for linear and circular edges,
+//!                                having a common point.
+//!         ChFi2d_FilletAlgo - iteration recursive method constructing 
+//!                             the fillet edge for any type of edges including
+//!                             ellipses and b-splines. 
+//!                             The edges may even have no common point.
+//!
+//! The algorithms ChFi2d_AnaFilletAlgo and ChFi2d_FilletAlgo may be used directly 
+//! or via this ChFi2d_FilletAPI class. This class chooses an appropriate algorithm
+//! analyzing the arguments (a wire or two edges).
+class ChFi2d_FilletAPI
+{
+public:
+
+  //! An empty constructor of the fillet algorithm.
+  //! Call a method Init() to initialize the algorithm
+  //! before calling of a Perform() method.
+  Standard_EXPORT ChFi2d_FilletAPI();
+
+  //! A constructor of a fillet algorithm: accepts a wire consisting of two edges in a plane.
+  Standard_EXPORT ChFi2d_FilletAPI(const TopoDS_Wire& theWire, 
+                                   const gp_Pln& thePlane);
+
+  //! A constructor of a fillet algorithm: accepts two edges in a plane.
+  Standard_EXPORT ChFi2d_FilletAPI(const TopoDS_Edge& theEdge1, 
+                                   const TopoDS_Edge& theEdge2, 
+                                   const gp_Pln& thePlane);
+
+  //! Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane.
+  Standard_EXPORT void Init(const TopoDS_Wire& theWire, 
+                            const gp_Pln& thePlane);
+
+  //! Initializes a fillet algorithm: accepts two edges in a plane.
+  Standard_EXPORT void Init(const TopoDS_Edge& theEdge1, 
+                            const TopoDS_Edge& theEdge2, 
+                            const gp_Pln& thePlane);
+
+  //! Constructs a fillet edge.
+  //! Returns true if at least one result was found.
+  Standard_EXPORT Standard_Boolean Perform(const Standard_Real theRadius);
+
+  //! Returns number of possible solutions.
+  //! <thePoint> chooses a particular fillet in case of several fillets 
+  //! may be constructed (for example, a circle intersecting a segment in 2 points).
+  //! Put the intersecting (or common) point of the edges.
+  Standard_EXPORT Standard_Integer NbResults(const gp_Pnt& thePoint);
+
+  //! Returns result (fillet edge, modified edge1, modified edge2), 
+  //! nearest to the given point <thePoint> if iSolution == -1
+  //! <thePoint> chooses a particular fillet in case of several fillets 
+  //! may be constructed (for example, a circle intersecting a segment in 2 points).
+  //! Put the intersecting (or common) point of the edges.
+  Standard_EXPORT TopoDS_Edge Result(const gp_Pnt& thePoint, 
+                                     TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2, 
+                                     const Standard_Integer iSolution = -1);
+
+private:
+
+  // Decides whether the input parameters may use an analytical algorithm
+  // for calculation of the fillets, or an iteration-recursive method is needed.
+  // The analytical solution is applicable for linear and circular edges 
+  // having a common point.
+  Standard_Boolean IsAnalytical(const TopoDS_Edge& theEdge1, 
+                                const TopoDS_Edge& theEdge2);
+
+  // Implementation of the fillet algorithm.
+  ChFi2d_FilletAlgo    myFilletAlgo;
+  ChFi2d_AnaFilletAlgo myAnaFilletAlgo;
+  Standard_Boolean     myIsAnalytical;
+};
+
+#endif // _CHFI2D_FILLETAPI_H_
\ No newline at end of file
diff --git a/src/ChFi2d/ChFi2d_FilletAlgo.cxx b/src/ChFi2d/ChFi2d_FilletAlgo.cxx
new file mode 100644 (file)
index 0000000..f6ed5e6
--- /dev/null
@@ -0,0 +1,814 @@
+// Copyright (c) 1999-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#include <ChFi2d_FilletAlgo.hxx>
+
+#include <GeomProjLib.hxx>
+#include <BRep_Tool.hxx>
+#include <Precision.hxx>
+#include <ElSLib.hxx>
+#include <ElCLib.hxx>
+
+#include <Geom2dAPI_ProjectPointOnCurve.hxx>
+#include <GeomAPI_ProjectPointOnCurve.hxx>
+#include <Geom2dAPI_InterCurveCurve.hxx>
+
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <TColStd_ListIteratorOfListOfReal.hxx>
+
+#include <gp_Circ.hxx>
+#include <Geom_Circle.hxx>
+#include <Geom2d_Line.hxx>
+
+#include <BRepBuilderAPI_MakeEdge.hxx>
+#include <BRepAdaptor_Curve.hxx>
+
+ChFi2d_FilletAlgo::ChFi2d_FilletAlgo()
+{
+
+}
+
+ChFi2d_FilletAlgo::ChFi2d_FilletAlgo(const TopoDS_Wire& theWire, const gp_Pln& thePlane) 
+{
+  Init(theWire, thePlane);
+}
+
+ChFi2d_FilletAlgo::ChFi2d_FilletAlgo(const TopoDS_Edge& theEdge1, 
+                                     const TopoDS_Edge& theEdge2, 
+                                     const gp_Pln& thePlane) 
+{
+  Init(theEdge1, theEdge2, thePlane);
+}
+
+void ChFi2d_FilletAlgo::Init(const TopoDS_Wire& theWire, const gp_Pln& thePlane) 
+{
+  TopoDS_Edge theEdge1, theEdge2;
+  TopoDS_Iterator itr(theWire);
+  for (; itr.More(); itr.Next())
+  {
+    if (theEdge1.IsNull())
+      theEdge1 = TopoDS::Edge(itr.Value());
+    else if (theEdge2.IsNull())
+      theEdge2 = TopoDS::Edge(itr.Value());
+    else
+      break;
+  }
+  if (theEdge1.IsNull() || theEdge2.IsNull())
+    Standard_ConstructionError::Raise("The fillet algorithms expects a wire consisting of two edges.");
+  Init(theEdge1, theEdge2, thePlane);
+}
+
+void ChFi2d_FilletAlgo::Init(const TopoDS_Edge& theEdge1, 
+                             const TopoDS_Edge& theEdge2, 
+                             const gp_Pln& thePlane)
+{
+  myPlane = new Geom_Plane(thePlane);
+
+  myEdgesExchnged = Standard_False;
+
+  BRepAdaptor_Curve aBAC1(theEdge1);
+  BRepAdaptor_Curve aBAC2(theEdge2);
+  if (aBAC1.GetType() < aBAC2.GetType()) 
+  { // first curve must be more complicated
+    myEdge1 = theEdge2;
+    myEdge2 = theEdge1;
+    myEdgesExchnged = Standard_True;
+  }     
+  else 
+  {
+    myEdge1 = theEdge1;
+    myEdge2 = theEdge2;
+  }
+
+  Handle(Geom_Curve) aCurve1 = BRep_Tool::Curve(myEdge1, myStart1, myEnd1);
+  Handle(Geom_Curve) aCurve2 = BRep_Tool::Curve(myEdge2, myStart2, myEnd2);
+
+  myCurve1 = GeomProjLib::Curve2d(aCurve1, myStart1, myEnd1, myPlane);
+  myCurve2 = GeomProjLib::Curve2d(aCurve2, myStart2, myEnd2, myPlane);
+
+  while (myCurve1->IsPeriodic() && myStart1 >= myEnd1)
+    myEnd1 += myCurve1->Period();
+  while (myCurve2->IsPeriodic() && myStart2 >= myEnd2)
+    myEnd2 += myCurve2->Period();
+  if (aBAC1.GetType() == aBAC2.GetType()) 
+  {
+    if (myEnd2 - myStart2 < myEnd1 - myStart1) 
+    { // first curve must be parametrically shorter
+      TopoDS_Edge anEdge = myEdge1;
+      myEdge1 = myEdge2;
+      myEdge2 = anEdge;
+      Handle(Geom2d_Curve) aCurve = myCurve1;
+      myCurve1 = myCurve2;
+      myCurve2 = aCurve;
+      Standard_Real a = myStart1;
+      myStart1 = myStart2;
+      myStart2 = a;
+      a = myEnd1;
+      myEnd1 = myEnd2;
+      myEnd2 = a;
+      myEdgesExchnged = Standard_True;
+    }
+  }
+}
+
+//! This function returns true if linear segment from start point of the 
+//! fillet arc to the end point is intersected by the first or second 
+//! curve: in this case fillet is invalid.
+static Standard_Boolean IsRadiusIntersected(const Handle(Geom2d_Curve)& theCurve, const Standard_Real theCurveMin, const double theCurveMax,
+                                            const gp_Pnt2d theStart, const gp_Pnt2d theEnd, const Standard_Boolean theStartConnected) 
+{
+  //Check the given start and end if they are identical. If yes
+  //return false
+  if (theStart.SquareDistance(theEnd) < Precision::SquareConfusion())
+  {
+    return Standard_False;
+  }
+  Handle(Geom2d_Line) line = new Geom2d_Line(theStart, gp_Dir2d(gp_Vec2d(theStart, theEnd)));
+  Geom2dAPI_InterCurveCurve anInter(theCurve, line, Precision::Confusion());
+  Standard_Integer a;
+  gp_Pnt2d aPoint;
+  for(a = anInter.NbPoints(); a > 0; a--) 
+  {
+    aPoint = anInter.Point(a);
+    Geom2dAPI_ProjectPointOnCurve aProjInt(aPoint, theCurve, theCurveMin, theCurveMax);
+    if (aProjInt.NbPoints() < 1 || aProjInt.LowerDistanceParameter() > Precision::Confusion()) 
+      continue; // point is not on edge
+
+    if (aPoint.Distance(theStart) < Precision::Confusion())
+    {
+      if (!theStartConnected) 
+        return Standard_True;
+    }
+    if (aPoint.Distance(theEnd) < Precision::Confusion()) 
+      return Standard_True;
+    if (gp_Vec2d(aPoint, theStart).IsOpposite(gp_Vec2d(aPoint, theEnd), Precision::Angular())) 
+      return Standard_True;
+  }
+  Handle(Geom2d_Curve) aCurve = theCurve;
+  for(a = anInter.NbSegments(); a > 0; a--) 
+  {
+    //anInter.Segment(a, aCurve); //not implemented (bug in OCC)
+    aPoint = aCurve->Value(aCurve->FirstParameter());
+
+    Geom2dAPI_ProjectPointOnCurve aProjInt(aPoint, theCurve, theCurveMin, theCurveMax);
+    if (aProjInt.NbPoints() && aProjInt.LowerDistanceParameter() < Precision::Confusion()) 
+    { // point is on edge
+      if (aPoint.Distance(theStart) < Precision::Confusion()) 
+        if (!theStartConnected) 
+          return Standard_True;
+      if (aPoint.Distance(theEnd) < Precision::Confusion()) 
+        return Standard_True;
+      if (gp_Vec2d(aPoint, theStart).IsOpposite(gp_Vec2d(aPoint, theEnd), Precision::Angular())) 
+        return Standard_True;
+    }
+    aPoint = aCurve->Value(aCurve->LastParameter());
+
+    aProjInt.Init(aPoint, theCurve, theCurveMin, theCurveMax);
+    if (aProjInt.NbPoints() && aProjInt.LowerDistanceParameter() < Precision::Confusion()) 
+    { // point is on edge
+      if (aPoint.Distance(theStart) < Precision::Confusion()) 
+        if (!theStartConnected) 
+          return Standard_True;
+      if (aPoint.Distance(theEnd) < Precision::Confusion()) 
+        return Standard_True;
+      if (gp_Vec2d(aPoint, theStart).IsOpposite(gp_Vec2d(aPoint, theEnd), Precision::Angular())) 
+        return Standard_True;
+    }
+  }
+  return Standard_False;
+}
+
+void ChFi2d_FilletAlgo::FillPoint(FilletPoint* thePoint, const Standard_Real theLimit) 
+{
+  
+  // on the intersection point
+  Standard_Boolean aValid = Standard_False;
+  Standard_Real aStep = Precision::Confusion();
+  gp_Pnt2d aCenter, aPoint; // center of fillet and point on curve1
+  Standard_Real aParam = thePoint->getParam();
+  if (theLimit < aParam) aStep = -aStep;
+  for(aValid = Standard_False; !aValid; aParam += aStep) 
+  {
+    if ((aParam - aStep - theLimit) * (aParam - theLimit) <= 0) 
+      break; // limit was exceeded
+    aStep *= 2;
+    gp_Vec2d aVec;
+    myCurve1->D1(aParam, aPoint, aVec);
+    if (aVec.SquareMagnitude() < Precision::Confusion()) 
+      continue;
+
+    gp_Vec2d aPerp(((myStartSide)?-1:1) * aVec.Y(), ((myStartSide)?1:-1) * aVec.X());
+    aPerp.Normalize();
+    aPerp.Multiply(myRadius);
+    aCenter = aPoint.Translated(aPerp);
+
+
+    Geom2dAPI_ProjectPointOnCurve aProjInt(aPoint, myCurve2, myStart2, myEnd2);
+    if (aProjInt.NbPoints() == 0 || aPoint.Distance(aProjInt.NearestPoint()) > Precision::Confusion()) 
+    {
+      aValid = Standard_True;
+      break;
+    }
+  }
+  if (aValid) 
+  {
+    thePoint->setParam(aParam);
+    thePoint->setCenter(aCenter);
+    aValid = !IsRadiusIntersected(myCurve2, myStart2, myEnd2, aPoint, aCenter, Standard_True);
+  }
+  
+  Geom2dAPI_ProjectPointOnCurve aProj(aCenter, myCurve2);
+  int a, aNB = aProj.NbPoints();
+  for(a = aNB; a > 0; a--) 
+  {
+    if (aPoint.SquareDistance(aProj.Point(a)) < Precision::Confusion()) 
+      continue;
+        
+    Standard_Boolean aValid2 = aValid;
+    if (aValid2) 
+      aValid2 = !IsRadiusIntersected(myCurve1, myStart1, myEnd1, aCenter, aProj.Point(a), Standard_False);
+
+    // checking the right parameter
+    Standard_Real aParam = aProj.Parameter(a);
+    while(myCurve2->IsPeriodic() && aParam < myStart2)
+      aParam += myCurve2->Period();
+
+    const Standard_Real d = aProj.Distance(a);
+    thePoint->appendValue(d * d - myRadius * myRadius, (aParam >= myStart2 && aParam <= myEnd2 && aValid2));
+    if (Abs(d - myRadius) < Precision::Confusion())
+      thePoint->setParam2(aParam);
+  }
+}
+
+void ChFi2d_FilletAlgo::FillDiff(FilletPoint* thePoint, Standard_Real theDiffStep, Standard_Boolean theFront) 
+{
+  Standard_Real aDelta = theFront?(theDiffStep):(-theDiffStep);
+  FilletPoint* aDiff = new FilletPoint(thePoint->getParam() + aDelta);
+  FillPoint(aDiff, aDelta * 999.);
+  if (!thePoint->calculateDiff(aDiff)) 
+  {
+    aDiff->setParam(thePoint->getParam() - aDelta);
+    FillPoint(aDiff,  - aDelta * 999);
+    thePoint->calculateDiff(aDiff);
+  }
+  delete aDiff;
+}
+
+// returns true, if at least one result was found
+Standard_Boolean ChFi2d_FilletAlgo::Perform(const Standard_Real theRadius) 
+{
+  myDegreeOfRecursion = 0;
+  myResultParams.Clear();
+  myResultOrientation.Clear();
+
+  Standard_Real aNBSteps;
+  Geom2dAdaptor_Curve aGAC(myCurve1);
+  switch (aGAC.GetType()) 
+  {
+  case GeomAbs_Line:
+    aNBSteps = 2;
+    break;
+  case GeomAbs_Circle:
+    aNBSteps = 4;
+    break;
+  case GeomAbs_Ellipse:
+    aNBSteps = 5;
+    break;
+  case GeomAbs_BSplineCurve:
+    aNBSteps = 2 + aGAC.Degree() * aGAC.NbPoles();
+    break;
+  default: // unknown: maximum
+    aNBSteps = 100;
+  }
+  //cout<<"aNBSteps = "<<aNBSteps<<endl;
+
+  myRadius = theRadius;
+  Standard_Real aParam, aStep, aDStep;
+  aStep = (myEnd1 - myStart1) / aNBSteps;
+  aDStep = 1.e-4 * aStep;
+
+  int aCycle;
+  for(aCycle = 2, myStartSide = Standard_False; aCycle; myStartSide = !myStartSide, aCycle--) 
+  {
+    FilletPoint *aLeft = NULL, *aRight;
+    
+    for(aParam = myStart1 + aStep; aParam < myEnd1 || Abs(myEnd1 - aParam) < Precision::Confusion(); aParam += aStep) 
+    {
+      if (!aLeft) 
+      {
+        aLeft = new FilletPoint(aParam - aStep);
+        FillPoint(aLeft, aParam);
+        FillDiff(aLeft, aDStep, Standard_True);
+      }
+      
+      aRight = new FilletPoint(aParam);
+      FillPoint(aRight, aParam - aStep);
+      FillDiff(aRight, aDStep, Standard_False);
+      
+      aLeft->FilterPoints(aRight);
+      PerformNewton(aLeft, aRight);
+      
+      delete aLeft;
+      aLeft = aRight;
+    }//for
+    delete aLeft;
+  }//for
+
+  return !myResultParams.IsEmpty();
+}
+
+Standard_Boolean ChFi2d_FilletAlgo::ProcessPoint(FilletPoint* theLeft, FilletPoint* theRight, Standard_Real theParameter) 
+{
+  if (theParameter >= theLeft->getParam() && theParameter < theRight->getParam()) 
+  {
+    Standard_Real aDX = (theRight->getParam() - theLeft->getParam());
+    if (theParameter - theLeft->getParam() < aDX/100.0) 
+    {
+      theParameter = theLeft->getParam() + aDX/100.0;
+    }
+    if (theRight->getParam() - theParameter < aDX/100.0) 
+    {
+      theParameter = theRight->getParam() - aDX/100.0;
+    }
+
+    // Protection on infinite loops.
+    myDegreeOfRecursion++;
+    Standard_Real diffx = 0.001 * aDX;
+    if (myDegreeOfRecursion > 100)
+    {
+      diffx *= 10.0;
+      if (myDegreeOfRecursion > 1000)
+      {
+        diffx *= 10.0;
+        if (myDegreeOfRecursion > 3000)
+        {
+          return Standard_True;
+        }
+      }
+    }
+
+    FilletPoint* aPoint1 = theLeft->Copy();
+    FilletPoint* aPoint2 = new FilletPoint(theParameter);
+    FillPoint(aPoint2, aPoint1->getParam());
+    FillDiff(aPoint2, diffx, Standard_True);
+    
+    aPoint1->FilterPoints(aPoint2);
+    PerformNewton(aPoint1, aPoint2);
+    aPoint2->FilterPoints(theRight);
+    PerformNewton(aPoint2, theRight);
+
+    delete aPoint1;
+    delete aPoint2;
+    return Standard_True;
+  }
+
+  return Standard_False;
+}
+
+void ChFi2d_FilletAlgo::PerformNewton(FilletPoint* theLeft, FilletPoint* theRight) 
+{
+  int a;
+  // check the left: if this is solution store it and remove it from the list of researching points of theLeft
+  a = theLeft->hasSolution(myRadius);
+  if (a) 
+  {
+    if (theLeft->isValid(a)) 
+    {
+      myResultParams.Append(theLeft->getParam());
+      myResultOrientation.Append(myStartSide);
+    }
+    return;
+  }
+
+  Standard_Real aDX = theRight->getParam() - theLeft->getParam();
+  if (aDX < 1.e-6 * Precision::Confusion())
+  {
+    a = theRight->hasSolution(myRadius);
+    if (a && theRight->isValid(a)) 
+    {
+      myResultParams.Append(theRight->getParam());
+      myResultOrientation.Append(myStartSide);
+    }
+    return;
+  }
+  for(a = 1; a <= theLeft->getNBValues(); a++) 
+  {
+    int aNear = theLeft->getNear(a);
+        
+    Standard_Real aA = (theRight->getDiff(aNear) - theLeft->getDiff(a)) / aDX;
+    Standard_Real aB = theLeft->getDiff(a) - aA * theLeft->getParam();
+    Standard_Real aC = theLeft->getValue(a) - theLeft->getDiff(a) * theLeft->getParam() + aA * theLeft->getParam() * theLeft->getParam() / 2.0;
+    Standard_Real aDet = aB * aB - 2.0 * aA * aC;
+    
+    if (Abs(aA) < Precision::Confusion()) 
+    { // linear case
+      //cout<<"###"<<endl;
+      if (Abs(aB) > 10e-20) 
+      {
+        Standard_Real aX0 = - aC / aB; // use extremum
+        if (aX0 > theLeft->getParam() && aX0 < theRight->getParam())
+          ProcessPoint(theLeft, theRight, aX0);
+      }
+      else 
+      {
+        ProcessPoint(theLeft, theRight, theLeft->getParam() + aDX / 2.0); // linear division otherwise
+      }
+    } 
+    else
+    {
+      if (Abs(aB) > Abs(aDet * 1000000.)) 
+      {        // possible floating point operations accurancy errors
+        //cout<<"*";
+        ProcessPoint(theLeft, theRight, theLeft->getParam() + aDX / 2.0); // linear division otherwise
+      } 
+      else
+      {
+        if (aDet > 0) 
+        { // two solutions
+          aDet = sqrt(aDet);
+          Standard_Boolean aRes = ProcessPoint(theLeft, theRight, (- aB + aDet) / aA);
+          if (!aRes) 
+            aRes = ProcessPoint(theLeft, theRight, (- aB - aDet) / aA);
+          if (!aRes) 
+            ProcessPoint(theLeft, theRight, theLeft->getParam() + aDX / 2.0); // linear division otherwise
+        } 
+        else 
+        {
+          //cout<<"%%%"<<endl;
+          Standard_Real aX0 = - aB / aA; // use extremum
+          if (aX0 > theLeft->getParam() && aX0 < theRight->getParam())
+            ProcessPoint(theLeft, theRight, aX0);
+          else 
+            ProcessPoint(theLeft, theRight, theLeft->getParam() + aDX / 2.0); // linear division otherwise
+        }
+      }
+    }
+  }//for
+}
+
+// returns number of possible solutions.
+int ChFi2d_FilletAlgo::NbResults(const gp_Pnt& thePoint)
+{
+  Standard_Real aX, aY;
+  gp_Pnt2d aTargetPoint2d;
+  ElSLib::PlaneParameters(myPlane->Pln().Position(), thePoint, aX, aY);
+  aTargetPoint2d.SetCoord(aX, aY);
+   
+  //iterate through all possible solutions.
+  int i = 1, nb = 0;
+  TColStd_ListIteratorOfListOfReal anIter(myResultParams);
+  for(; anIter.More(); anIter.Next(), i++) 
+  {
+    myStartSide = (myResultOrientation.Value(i)) ? Standard_True : Standard_False;
+    FilletPoint *aPoint = new FilletPoint(anIter.Value());
+    FillPoint(aPoint, anIter.Value() + 1.);
+    if (aPoint->hasSolution(myRadius)) 
+      nb++;
+    delete aPoint;
+  }//for
+
+  return nb;
+}
+
+// returns result (fillet edge, modified edge1, modified edge2), neares to the given point <thePoint>
+TopoDS_Edge ChFi2d_FilletAlgo::Result(const gp_Pnt& thePoint, TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2, const int iSolution) 
+{
+  TopoDS_Edge aResult;
+  gp_Pnt2d aTargetPoint2d;
+  Standard_Real aX, aY;
+  ElSLib::PlaneParameters(myPlane->Pln().Position(), thePoint, aX, aY);
+  aTargetPoint2d.SetCoord(aX, aY);
+   
+  // choose the nearest circle
+  Standard_Real aDistance = 0.0, aP;
+  FilletPoint *aNearest;
+  int a, iSol = 1;
+  TColStd_ListIteratorOfListOfReal anIter(myResultParams);
+  for(aNearest = NULL, a = 1; anIter.More(); anIter.Next(), a++) 
+  {
+    myStartSide = (myResultOrientation.Value(a))?Standard_True:Standard_False;
+    FilletPoint *aPoint = new FilletPoint(anIter.Value());
+    FillPoint(aPoint, anIter.Value() + 1.);
+    if (!aPoint->hasSolution(myRadius))
+    {
+      delete aPoint;
+      continue;
+    }
+    aP = DBL_MAX;
+    if (iSolution == -1)
+    {
+      aP = Abs(aPoint->getCenter().Distance(aTargetPoint2d) - myRadius);
+    }
+    else if (iSolution == iSol)
+    {
+      aP = 0.0;
+    }
+    if (!aNearest || aP < aDistance) 
+    {
+      aNearest = aPoint;
+      aDistance = aP;
+    } 
+    else 
+    {
+      delete aPoint;
+    }
+    if (iSolution == iSol)
+      break;
+    iSol++;
+  }//for
+   
+  if (!aNearest) 
+    return aResult;
+   
+  // create circle edge
+  gp_Pnt aCenter = ElSLib::PlaneValue(aNearest->getCenter().X(), aNearest->getCenter().Y(), myPlane->Pln().Position());
+  Handle(Geom_Circle) aCircle = new Geom_Circle(gp_Ax2(aCenter, myPlane->Pln().Axis().Direction()), myRadius);
+  gp_Pnt2d aPoint2d1, aPoint2d2;
+  myCurve1->D0(aNearest->getParam(), aPoint2d1);
+  myCurve2->D0(aNearest->getParam2(), aPoint2d2);
+  gp_Pnt aPoint1 = ElSLib::PlaneValue(aPoint2d1.X(), aPoint2d1.Y(), myPlane->Pln().Position());
+  gp_Pnt aPoint2 = ElSLib::PlaneValue(aPoint2d2.X(), aPoint2d2.Y(), myPlane->Pln().Position());
+
+  GeomAPI_ProjectPointOnCurve aProj(thePoint, aCircle);
+  Standard_Real aTargetParam = aProj.LowerDistanceParameter();
+  gp_Pnt aPointOnCircle = aProj.NearestPoint();
+
+  // There is a bug in Open CASCADE in calculation of nearest point to a circle near the parameter 0.0
+  // Therefore I check this extrema point manually:
+  gp_Pnt p0 = ElCLib::Value(0.0, aCircle->Circ());
+  if (p0.Distance(thePoint) < aPointOnCircle.Distance(thePoint))
+  {
+    aTargetParam = 0.0;
+    aPointOnCircle = p0;
+  }
+
+  aProj.Perform(aPoint1);
+  Standard_Real aParam1 = aProj.LowerDistanceParameter();
+  aProj.Perform(aPoint2);
+  Standard_Real aParam2 = aProj.LowerDistanceParameter();
+  Standard_Boolean aIsOut = ((aParam1 < aTargetParam && aParam2 < aTargetParam) || (aParam1 > aTargetParam && aParam2 > aTargetParam));
+  if (aParam1 > aParam2) 
+    aIsOut = !aIsOut;
+  BRepBuilderAPI_MakeEdge aBuilder(aCircle->Circ(), aIsOut ? aParam2 : aParam1, aIsOut? aParam1 : aParam2);
+  aResult = aBuilder.Edge();
+
+  // divide edges
+  Standard_Real aStart, anEnd;
+  Handle(Geom_Curve) aCurve = BRep_Tool::Curve(myEdge1, aStart, anEnd);
+  gp_Vec aDir;
+  aCurve->D1(aNearest->getParam(), aPoint1, aDir);
+
+  gp_Vec aCircleDir;
+  aCircle->D1(aParam1, aPoint1, aCircleDir);
+    
+  if ((aCircleDir.Angle(aDir) > M_PI / 2.0) ^ aIsOut)
+    aStart = aNearest->getParam();
+  else
+    anEnd = aNearest->getParam();
+
+  //Check the case when start and end are identical. This happens
+  //when the edge decreases to size 0. Old ww5 allows such
+  //cases. So we are again bug compatible
+  if (fabs(aStart - anEnd) < Precision::Confusion())
+    anEnd = aStart + Precision::Confusion();
+  //Divide edge
+  BRepBuilderAPI_MakeEdge aDivider1(aCurve, aStart, anEnd);
+  if (myEdgesExchnged) 
+    theEdge2 = aDivider1.Edge();
+  else 
+    theEdge1 = aDivider1.Edge();
+
+  aCurve = BRep_Tool::Curve(myEdge2, aStart, anEnd);
+  aCurve->D1(aNearest->getParam2(), aPoint2, aDir);
+   
+  aCircle->D1(aParam2, aPoint2, aCircleDir);
+
+  if ((aCircleDir.Angle(aDir) > M_PI / 2.0) ^ (!aIsOut))
+    aStart = aNearest->getParam2();
+  else
+    anEnd = aNearest->getParam2();
+
+  //Check the case when start and end are identical. This happens
+  //when the edge decreases to size 0. Old ww5 allows such
+  //cases. So we are again bug compatible
+  if (fabs(aStart - anEnd) < Precision::Confusion())
+    anEnd = aStart + Precision::Confusion();
+  BRepBuilderAPI_MakeEdge aDivider2(aCurve, aStart, anEnd);
+  if (myEdgesExchnged) 
+    theEdge1 = aDivider2.Edge();
+  else 
+    theEdge2 = aDivider2.Edge();
+
+  delete aNearest;
+  return aResult;
+}
+
+void FilletPoint::appendValue(Standard_Real theValue, Standard_Boolean theValid) 
+{
+  Standard_Integer a;
+  for(a = 1; a <= myV.Length(); a++) 
+  {
+    if (theValue < myV.Value(a)) 
+    {
+      myV.InsertBefore(a, theValue);
+      myValid.InsertBefore(a, (int)theValid);
+      return;
+    }
+  }
+  myV.Append(theValue);
+  myValid.Append((int)theValid);
+}
+
+Standard_Boolean FilletPoint::calculateDiff(FilletPoint* thePoint) 
+{
+  Standard_Integer a;
+  Standard_Boolean aDiffsSet = (myD.Length() != 0);
+  Standard_Real aDX = thePoint->getParam() - myParam, aDY = 0.0;
+  if (thePoint->myV.Length() == myV.Length()) 
+  { // absolutely the same points
+    for(a = 1; a <= myV.Length(); a++) 
+    {
+      aDY = thePoint->myV.Value(a) - myV.Value(a);
+      if (aDiffsSet) 
+        myD.SetValue(a, aDY / aDX);
+      else 
+        myD.Append(aDY / aDX);
+    }
+    return Standard_True;
+  }
+  // between the diffeerent points searching for nearest analogs
+  Standard_Integer b;
+  for(a = 1; a <= myV.Length(); a++) 
+  {
+    for(b = 1; b <= thePoint->myV.Length(); b++) 
+    {
+      if (b == 1 || Abs(thePoint->myV.Value(b) - myV.Value(a)) < Abs(aDY))
+        aDY = thePoint->myV.Value(b) - myV.Value(a);
+    }
+    if (aDiffsSet) 
+    {
+      if (Abs(aDY / aDX) < Abs(myD.Value(a)))
+        myD.SetValue(a, aDY / aDX);
+    } 
+    else 
+    {
+      myD.Append(aDY / aDX);
+    }
+  }//for
+    
+  return Standard_False;
+}
+
+void FilletPoint::FilterPoints(FilletPoint* thePoint) 
+{
+  Standard_Integer a, b;
+  TColStd_SequenceOfReal aDiffs;
+  Standard_Real aY, aY2, aDX = thePoint->getParam() - myParam;
+  for(a = 1; a <= myV.Length(); a++) 
+  {
+    // searching for near point from thePoint
+    Standard_Integer aNear = 0;
+    Standard_Real aDiff = aDX * 10000.;
+    aY = myV.Value(a) + myD.Value(a) * aDX;
+    for(b = 1; b <= thePoint->myV.Length(); b++) 
+    {
+      // calculate hypothesis value of the Y2 with the constant first and second derivative
+      aY2 = aY + aDX * (thePoint->myD.Value(b) - myD.Value(a)) / 2.0;
+      if (aNear == 0 || Abs(aY2 - thePoint->myV.Value(b)) < Abs(aDiff)) 
+      {
+        aNear = b;
+        aDiff = aY2 - thePoint->myV.Value(b);
+      }
+    }//for b...
+
+    if (aNear) 
+    {
+      if (myV.Value(a) * thePoint->myV.Value(aNear) > 0) 
+      {// the same sign at the same sides of the interval
+        if (myV.Value(a) * myD.Value(a) > 0) 
+        {
+          if (Abs(myD.Value(a)) > Precision::Confusion()) 
+            aNear = 0;
+        } 
+        else 
+        {
+          if (Abs(myV.Value(a)) > Abs(thePoint->myV.Value(aNear)))
+            if (thePoint->myV.Value(aNear) * thePoint->myD.Value(aNear) < 0 && Abs(thePoint->myD.Value(aNear)) > Precision::Confusion())
+            {
+              aNear = 0;
+            }
+        }
+      }
+    }//if aNear
+
+    if (aNear) 
+    {
+      if (myV.Value(a) * thePoint->myV.Value(aNear) > 0) 
+      {
+        if ((myV.Value(a) + myD.Value(a) * aDX) * myV.Value(a) > Precision::Confusion() &&
+            (thePoint->myV.Value(aNear) + thePoint->myD.Value(aNear) * aDX) * thePoint->myV.Value(aNear) > Precision::Confusion())
+        {
+          aNear = 0;
+        }
+      }
+    }//if aNear
+    
+    if (aNear)
+    {
+      if (Abs(aDiff / aDX) > 1.e+7) 
+      {
+        aNear = 0;
+      }
+    }
+
+    if (aNear == 0) 
+    {  // there is no near: remove it from the list
+      myV.Remove(a);
+      myD.Remove(a);
+      myValid.Remove(a);
+      a--;
+    } 
+    else 
+    {
+      Standard_Boolean aFound = Standard_False;
+      for(b = 1; b <= myNear.Length(); b++) 
+      {
+        if (myNear.Value(b) == aNear) 
+        {
+          if (Abs(aDiffs.Value(b)) < Abs(aDiff)) 
+          { // return this 'near'
+            aFound = Standard_True;
+            myV.Remove(a);
+            myD.Remove(a);
+            myValid.Remove(a);
+            a--;
+            break;
+          } 
+          else 
+          { // remove the old 'near'
+            myV.Remove(b);
+            myD.Remove(b);
+            myValid.Remove(b);
+            myNear.Remove(b);
+            aDiffs.Remove(b);
+            a--;
+            break;
+          }
+        }
+      }//for b...
+      if (!aFound) 
+      {
+        myNear.Append(aNear);
+        aDiffs.Append(aDiff);
+      }
+    }//else
+  }//for a...
+}
+
+FilletPoint* FilletPoint::Copy() 
+{
+  FilletPoint* aCopy = new FilletPoint(myParam);
+  Standard_Integer a;
+  for(a = 1; a <= myV.Length(); a++) 
+  {
+    aCopy->myV.Append(myV.Value(a));
+    aCopy->myD.Append(myD.Value(a));
+    aCopy->myValid.Append(myValid.Value(a));
+  }
+  return aCopy;
+}
+
+int FilletPoint::hasSolution(const Standard_Real theRadius) 
+{
+  Standard_Integer a;
+  for(a = 1; a <= myV.Length(); a++) 
+  {
+    if (Abs(sqrt(Abs(Abs(myV.Value(a)) + theRadius * theRadius)) - theRadius) < Precision::Confusion()) 
+      return a;
+  }
+  return 0;
+}
+
+void FilletPoint::remove(int theIndex) 
+{
+  myV.Remove(theIndex);
+  myD.Remove(theIndex);
+  myValid.Remove(theIndex);
+  myNear.Remove(theIndex);
+}
diff --git a/src/ChFi2d/ChFi2d_FilletAlgo.hxx b/src/ChFi2d/ChFi2d_FilletAlgo.hxx
new file mode 100644 (file)
index 0000000..64f9a6d
--- /dev/null
@@ -0,0 +1,226 @@
+// Created on: 2013-05-20
+// Created by: Mikhail PONIKAROV
+// Copyright (c) 2003-2012 OPEN CASCADE SAS
+//
+// The content of this file is subject to the Open CASCADE Technology Public
+// License Version 6.5 (the "License"). You may not use the content of this file
+// except in compliance with the License. Please obtain a copy of the License
+// at http://www.opencascade.org and read it completely before using this file.
+//
+// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
+// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
+//
+// The Original Code and all software distributed under the License is
+// distributed on an "AS IS" basis, without warranty of any kind, and the
+// Initial Developer hereby disclaims all such warranties, including without
+// limitation, any warranties of merchantability, fitness for a particular
+// purpose or non-infringement. Please see the License for the specific terms
+// and conditions governing the rights and limitations under the License.
+
+#ifndef _FILLETALGO_H_
+#define _FILLETALGO_H_
+
+#include <TopoDS_Edge.hxx>
+#include <TopoDS_Wire.hxx>
+#include <gp_Pnt.hxx>
+#include <Geom2d_Curve.hxx>
+#include <Geom_Plane.hxx>
+#include <TColStd_ListOfReal.hxx>
+#include <TColStd_SequenceOfReal.hxx>
+#include <TColStd_SequenceOfInteger.hxx>
+
+class FilletPoint;
+
+//! Algorithm that creates fillet edge: arc tangent to two edges in the start
+//! and in the end vertices. Initial edges must be located on the plane and 
+//! must be connected by the end or start points (shared vertices are not 
+//! obligatory). Created fillet arc is created with the given radius, that is
+//! useful in sketcher applications.
+//! 
+//! The algorithm is iterative that allows to create fillet on any curves
+//! of initial edges, that supports projection of point and C2 continuous.
+//! Principles of algorithm can de reduced to the Newton method:
+//! 1. Splitting initial edge into N segments where probably only 1 root can be
+//!    found. N depends on the complexity of the underlying curve.
+//! 2. On each segment compute value and derivative of the function:
+//!    - argument of the function is the parameter on the curve
+//!    - take point on the curve by the parameter: point of tangency
+//!    - make center of fillet: perpendicular vector from the point of tagency
+//!    - make projection from the center to the second curve
+//!    - length of the projection minus radius of the fillet is result of the 
+//!      function
+//!    - derivative of this function in the point is computed by value in 
+//!      point with small shift
+//! 3. Using Newton search method take the point on the segment where function
+//!    value is most close to zero. If it is not enough close, step 2 and 3 are
+//!    repeated taking as start or end point the found point.
+//! 4. If solution is found, result is created on point on root of the function
+//!    (as a start point), point of the projection onto second curve (as an end 
+//!    point) and center of arc in found center. Initial edges are cutted by
+//!    the start and end point of tangency.
+class ChFi2d_FilletAlgo 
+{
+public:
+
+  //! An empty constructor of the fillet algorithm.
+  //! Call a method Init() to initialize the algorithm
+  //! before calling of a Perform() method.
+  Standard_EXPORT ChFi2d_FilletAlgo();
+
+  //! A constructor of a fillet algorithm: accepts a wire consisting of two edges in a plane.
+  Standard_EXPORT ChFi2d_FilletAlgo(const TopoDS_Wire& theWire, 
+                                    const gp_Pln& thePlane);
+
+  //! A constructor of a fillet algorithm: accepts two edges in a plane.
+  Standard_EXPORT ChFi2d_FilletAlgo(const TopoDS_Edge& theEdge1, 
+                                    const TopoDS_Edge& theEdge2, 
+                                    const gp_Pln& thePlane);
+
+  //! Initializes a fillet algorithm: accepts a wire consisting of two edges in a plane.
+  Standard_EXPORT void Init(const TopoDS_Wire& theWire, 
+                            const gp_Pln& thePlane);
+
+  //! Initializes a fillet algorithm: accepts two edges in a plane.
+  Standard_EXPORT void Init(const TopoDS_Edge& theEdge1, 
+                            const TopoDS_Edge& theEdge2, 
+                            const gp_Pln& thePlane);
+
+  //! Constructs a fillet edge.
+  //! Returns true, if at least one result was found
+  Standard_EXPORT Standard_Boolean Perform(const Standard_Real theRadius);
+
+  //! Returns number of possible solutions.
+  //! <thePoint> chooses a particular fillet in case of several fillets 
+  //! may be constructed (for example, a circle intersecting a segment in 2 points).
+  //! Put the intersecting (or common) point of the edges.
+  Standard_EXPORT Standard_Integer NbResults(const gp_Pnt& thePoint);
+
+  //! Returns result (fillet edge, modified edge1, modified edge2), 
+  //! neares to the given point <thePoint> if iSolution == -1.
+  //! <thePoint> chooses a particular fillet in case of several fillets 
+  //! may be constructed (for example, a circle intersecting a segment in 2 points).
+  //! Put the intersecting (or common) point of the edges.
+  Standard_EXPORT TopoDS_Edge Result(const gp_Pnt& thePoint, 
+                                     TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2, 
+                                     const Standard_Integer iSolution = -1);
+
+private:
+  //! Computes the value the function in the current point.
+  //! <theLimit> is end parameter of the segment
+  void FillPoint(FilletPoint*, const Standard_Real theLimit);
+  //! Computes the derivative value of the function in the current point.
+  //! <theDiffStep> is small step for approximate derivative computation
+  //! <theFront> is direction of the step: from or reverced
+  void FillDiff(FilletPoint*, Standard_Real theDiffStep, Standard_Boolean theFront);
+  //! Using Newton methods computes optimal point, that can be root of the
+  //! function taking into account two input points, functions value and derivatives.
+  //! Performs iteration until root is found or failed to find root.
+  //! Stores roots in myResultParams.
+  void PerformNewton(FilletPoint*, FilletPoint*);
+  //! Splits segment by the parameter and calls Newton method for both segments.
+  //! It supplies recursive iterations of the Newthon methods calls 
+  //! (PerformNewton calls this function and this calls Netwton two times).
+  Standard_Boolean ProcessPoint(FilletPoint*, FilletPoint*, Standard_Real);
+
+  //! Initial edges where the fillet must be computed.
+  TopoDS_Edge myEdge1, myEdge2;
+  //! Plane where fillet arc must be created.
+  Handle(Geom_Plane) myPlane;
+  //! Underlying curves of the initial edges
+  Handle(Geom2d_Curve) myCurve1, myCurve2;
+  //! Start and end parameters of curves of initial edges.
+  Standard_Real myStart1, myEnd1, myStart2, myEnd2, myRadius;
+  //! List of params where roots were found.
+  TColStd_ListOfReal myResultParams;
+  //! sequence of 0 or 1: position of the fillet relatively to the first curve
+  TColStd_SequenceOfInteger myResultOrientation;
+  //! position of the fillet relatively to the first curve
+  Standard_Boolean myStartSide;
+  //! are initial edges where exchanged in the beginning: to make first edge 
+  //! more simple and minimize number of iterations
+  Standard_Boolean myEdgesExchnged;
+  //! Number to avoid infinity recursion: indicates how deep the recursion is performed.
+  Standard_Integer myDegreeOfRecursion;
+};
+
+//! Private class. Corresponds to the point on the first curve, computed
+//! fillet function and derivative on it.
+class FilletPoint 
+{
+public:
+  //! Creates a point on a first curve by parameter on this curve.
+  FilletPoint(Standard_Real theParam) {myParam = theParam;}
+
+  //! Changes the point position by changing point parameter on the first curve.
+  void setParam(Standard_Real theParam) {myParam = theParam;}
+  //! Returns the point parameter on the first curve.
+  Standard_Real getParam() const {return myParam;}
+
+  //! Returns number of found values of function in this point.
+  Standard_Integer getNBValues() {return myV.Length();}
+  //! Returns value of function in this point.
+  Standard_Real getValue(int theIndex) {return myV.Value(theIndex);}
+  //! Returns derivatives of function in this point.
+  Standard_Real getDiff(int theIndex) {return myD.Value(theIndex);}
+  //! Returns true if function is valid (rediuses vectors of fillet do not intersect any curve).
+  Standard_Boolean isValid(int theIndex) {return (Standard_Boolean)myValid.Value(theIndex);}
+  //! Returns the index of the nearest value
+  int getNear(int theIndex) {return myNear.Value(theIndex);}
+
+  //! Defines the parameter of the projected point on the second curve.
+  void setParam2(const Standard_Real theParam2) {myParam2 = theParam2;}
+  //! Returns the parameter of the projected point on the second curve.
+  Standard_Real getParam2() { return myParam2 ; }
+
+  //! Center of the fillet.
+  void setCenter(const gp_Pnt2d thePoint) {myCenter = thePoint;}
+  //! Center of the fillet.
+  const gp_Pnt2d getCenter() {return myCenter;}
+
+  //! Appends value of the function.
+  void appendValue(Standard_Real theValue, Standard_Boolean theValid);
+
+  //! Computes difference between this point and the given. Stores difference in myD.
+  Standard_Boolean calculateDiff(FilletPoint*);
+  //! Filters out the values and leaves the most optimal one.
+  void FilterPoints(FilletPoint*);
+
+  //! Returns a pointer to created copy of the point
+  //! warning: this is not the full copy! Copies only: myParam, myV, myD, myValid
+  FilletPoint* Copy(); 
+  //! Returns the index of the solution or zero if there is no solution
+  Standard_Integer hasSolution(Standard_Real theRadius);
+  //! For debug only
+  Standard_Real LowerValue() 
+  {
+    Standard_Integer a, aResultIndex = 0;
+    Standard_Real aValue;
+    for(a = myV.Length(); a > 0; a--) 
+    {
+      if (aResultIndex == 0 || Abs(aValue) > Abs(myV.Value(a))) 
+      {
+        aResultIndex = a;
+        aValue = myV.Value(a);
+      }
+    }
+    return aValue;
+  }
+  //! Removes the found value by the given index.
+  void remove(Standard_Integer theIndex);
+
+private:
+  //! Parameter on the first curve (start fillet point).
+  Standard_Real myParam;
+  //! Parameter on the second curve (end fillet point).
+  Standard_Real myParam2;
+  //! Values and derivative values of the fillet function.
+  //! May be several if there are many projections on the second curve.
+  TColStd_SequenceOfReal myV, myD;
+  //! Center of the fillet arc.
+  gp_Pnt2d myCenter;
+  //! Flags for storage the validity of solutions. Indexes corresponds to indexes
+  //! in sequences myV, myD.
+  TColStd_SequenceOfInteger myValid, myNear;
+};
+
+#endif // _FILLETALGO_H_
index c344b32..2935057 100755 (executable)
@@ -1 +1,9 @@
 ChFi2d_Builder_0.cxx
+ChFi2d_FilletAlgo.hxx
+ChFi2d_FilletAlgo.cxx
+ChFi2d_AnaFilletAlgo.hxx
+ChFi2d_AnaFilletAlgo.cxx
+ChFi2d_ChamferAPI.hxx
+ChFi2d_ChamferAPI.cxx
+ChFi2d_FilletAPI.hxx
+ChFi2d_FilletAPI.cxx
diff --git a/tests/fillet2d/begin b/tests/fillet2d/begin
new file mode 100644 (file)
index 0000000..543f9f8
--- /dev/null
@@ -0,0 +1,15 @@
+# File : begin
+if { [array get Draw_Groups "TOPOLOGY Check commands"] == "" } {
+    pload TOPTEST
+    pload VISUALIZATION
+}
+
+# to prevent loops limit to 16 minutes
+cpulimit 1000
+
+if { [info exists imagedir] == 0 } {
+   set imagedir .
+}
+if { [info exists test_image] == 0 } {
+   set test_image photo
+}
diff --git a/tests/fillet2d/chamfer2d/A1 b/tests/fillet2d/chamfer2d/A1
new file mode 100644 (file)
index 0000000..0ad5851
--- /dev/null
@@ -0,0 +1,14 @@
+# Edge 1
+line l1 0 0 0 1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 0 0 0 1 0
+mkedge e2 l2 0 100
+
+# Chamfer
+chamfer2d result e1 e2 10 20
+
+set length 22.3607
+
+set 2dviewer 1
diff --git a/tests/fillet2d/end b/tests/fillet2d/end
new file mode 100644 (file)
index 0000000..0e6aecd
--- /dev/null
@@ -0,0 +1,191 @@
+if { [info exists square] } {
+    set prop "square"
+    set mass $square
+    if { [info exists tol_square] } {
+      regexp {Mass +: +([-0-9.+eE]+)} [sprops result $tol_square] full m
+    } else {
+      regexp {Mass +: +([-0-9.+eE]+)} [sprops result] full m
+    }
+}
+if { [info exists length] } {
+    set prop "length"
+    set mass $length
+    regexp {Mass +: +([-0-9.+eE]+)} [lprops result] full m      
+
+    puts "checksection"
+    puts [checksection result]
+}
+
+#if mass (length or square) is empty in test case then result should be an empty shape.
+if { [info exists mass] } {
+    puts "checkshape"
+    checkshape result
+      
+    if { [string compare "$mass" "empty"] != 0 } {
+      if { $m == 0 } {
+        puts "Error : The command is not valid. The $prop is 0."
+      }
+      if { $mass > 0 } {
+        puts "The expected $prop is $mass"
+      }
+      #check of change of square is < 1%
+      if { ($mass != 0 && [expr 1.*abs($mass - $m)/$mass] > 0.01) || ($mass == 0 && $m != 0) } {
+        puts "Error : The $prop of result shape is $m"
+      }
+    } else {
+      if { $m != 0 } {
+        puts "Error : The command is not valid. The $prop is $m"
+      }
+    }
+}
+
+set glob_inf [info global nb_*_good]
+if { [regexp "nb_.*_good" $glob_inf] == 1 } {
+    if { [info exists nbsh_t] } {
+      set nb_info [nbshapes result -t]
+    } else {
+      set nb_info [nbshapes result]
+    }
+}
+
+if { [info exists nb_v_good] } {
+    regexp {VERTEX +: +([-0-9.+eE]+)} $nb_info full nb_v
+    if { ${nb_v} != ${nb_v_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_v_good} vertices instead of ${nb_v}"
+    } else {
+        puts "Result shape contains ${nb_v} vertices"
+    }
+}
+
+if { [info exists nb_e_good] } {
+    regexp {EDGE +: +([-0-9.+eE]+)} $nb_info full nb_e
+    if { ${nb_e} != ${nb_e_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_e_good} edges instead of ${nb_e}"
+    } else {
+        puts "Result shape contains ${nb_e} edges"
+    }
+}
+
+if { [info exists nb_w_good] } {
+    regexp {WIRE +: +([-0-9.+eE]+)} $nb_info full nb_w
+    if { ${nb_w} != ${nb_w_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_w_good} wires instead of ${nb_w}"
+    } else {
+        puts "Result shape contains ${nb_w} wires"
+    }
+}
+
+if { [info exists nb_f_good] } {
+    regexp {FACE +: +([-0-9.+eE]+)} $nb_info full nb_f
+    if { ${nb_f} != ${nb_f_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_f_good} faces instead of ${nb_f}"
+    } else {
+        puts "Result shape contains ${nb_f} faces"
+    }
+}
+
+if { [info exists nb_sh_good] } {
+    regexp {SHELL +: +([-0-9.+eE]+)} $nb_info full nb_sh
+    if { ${nb_sh} != ${nb_sh_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_sh_good} shells instead of ${nb_sh}"
+    } else {
+        puts "Result shape contains ${nb_sh} shells"
+    }
+}
+
+if { [info exists nb_sol_good] } {
+    regexp {SOLID +: +([-0-9.+eE]+)} $nb_info full nb_sol
+    if { ${nb_sol} != ${nb_sol_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_sol_good} solids instead of ${nb_sol}"
+    } else {
+        puts "Result shape contains ${nb_sol} solids"
+    }
+}
+
+if { [info exists nb_compsol_good] } {
+    regexp {COMPSOLID +: +([-0-9.+eE]+)} $nb_info full nb_compsol
+    if { ${nb_compsol} != ${nb_compsol_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_compsol_good} compsolids instead of ${nb_compsol}"
+    } else {
+        puts "Result shape contains ${nb_compsol} compsolids"
+    }
+}
+
+if { [info exists nb_compound_good] } {
+    regexp {COMPOUND +: +([-0-9.+eE]+)} $nb_info full nb_compound
+    if { ${nb_compound} != ${nb_compound_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_compound_good} compounds instead of ${nb_compound}"
+    } else {
+        puts "Result shape contains ${nb_compound} compounds"
+    }
+}
+
+if { [info exists nb_shape_good] } {
+    regexp {SHAPE +: +([-0-9.+eE]+)} $nb_info full nb_shape
+    if { ${nb_shape} != ${nb_shape_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_shape_good} shapes instead of ${nb_shape}"
+    } else {
+        puts "Result shape contains ${nb_shape} shapes"
+    }
+}
+
+if { [info exists nb_fe_good] && [info exists nb_fe] } {
+    if { ${nb_fe} != ${nb_fe_good} } {
+        puts "Error : Result shape is WRONG because it must contains ${nb_shape_good} shapes instead of ${nb_shape}"
+    } else {
+        puts "Result shape contains ${nb_fe} free edges"
+    }
+}
+
+if { [info exists rel_tol] } {
+   puts "\nChecking triangulation area (triarea command)..."
+   set rel_err [expr abs([CheckTriArea result $area_eps])]
+   if { $rel_err > $rel_tol } {
+      puts "Error   : area by triangles differs from the actual area by $rel_err %"
+   } else {
+      if { $rel_tol > 1 && $rel_tol < 100 } {
+        puts "Error: Improvement: The current area difference is $rel_err instead of $rel_tol"
+      }
+   }
+}
+
+if { [isdraw result] } {
+  if { [info exists 2dviewer] } {
+    clear
+    smallview
+    donly result
+    fit
+    xwd $imagedir/${test_image}.png
+  }
+  if { [info exists 3dviewer] } {
+    #for multiview support: dump result from all opened views
+    set view_str [vviewlist long]
+    set view_list [regexp -all -inline {\S+} $view_str]
+    foreach {view_name} $view_list {
+      vactivate $view_name
+      vclear
+      vdisplay result
+      vsetdispmode 1
+      vfit
+      vzfit
+      vdump $imagedir/${test_image}_[regsub -all {/} $view_name {_}].png
+    }
+  }
+}
+
+if { [info exists only_screen] } {
+  #for multiview support: dump result from all opened views
+  set view_str [vviewlist long]
+  set view_list [regexp -all -inline {\S+} $view_str]
+  foreach {view_name} $view_list {
+    vactivate $view_name
+    vdump $imagedir/${test_image}_[regsub -all {/} $view_name {_}].png
+  }
+}
+
+if { [info exists only_screen_axo] } {
+   xwd $imagedir/${test_image}.png
+}
+
+# to end a test script
+puts "TEST COMPLETED"
diff --git a/tests/fillet2d/fillet2d/A1 b/tests/fillet2d/fillet2d/A1
new file mode 100644 (file)
index 0000000..feabba5
--- /dev/null
@@ -0,0 +1,14 @@
+# Edge 1
+line l1 0 0 0 1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 0 0 0 1 0
+mkedge e2 l2 0 100
+
+# Fillet (Newton)
+fillet2d result e1 e2 10
+
+set length 15.708
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A2 b/tests/fillet2d/fillet2d/A2
new file mode 100644 (file)
index 0000000..e40843d
--- /dev/null
@@ -0,0 +1,17 @@
+# Edge 1
+line l1 100 0 0 -1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 0 0 0 1 0
+mkedge e2 l2 0 100
+
+# Make wire
+wire w e1 e2
+
+# Fillet (analytical)
+fillet2d result w 10
+
+set length 195.708
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A3 b/tests/fillet2d/fillet2d/A3
new file mode 100644 (file)
index 0000000..bee9931
--- /dev/null
@@ -0,0 +1,17 @@
+# Edge 1
+line l1 200 0 0 -1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+circle c2 50 0 0 0 0 1 50
+mkedge e2 c2 0 3.14159265358979323846
+
+# Make wire
+wire w e1 e2
+
+# Fillet (analytical)
+fillet2d result w 10
+
+set length 253.58
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A4 b/tests/fillet2d/fillet2d/A4
new file mode 100644 (file)
index 0000000..1da8a70
--- /dev/null
@@ -0,0 +1,17 @@
+# Edge 1
+circle c1 50 0 0 0 0 1 50
+mkedge e1 c1 0 3.14159265358979323846
+
+# Edge 2
+line l2 0 0 0 -1 0 0
+mkedge e2 l2 0 100
+
+# Make wire
+wire w e1 e2
+
+# Fillet (analytical)
+fillet2d result w 10
+
+set length 253.58
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A5 b/tests/fillet2d/fillet2d/A5
new file mode 100644 (file)
index 0000000..1d3b678
--- /dev/null
@@ -0,0 +1,17 @@
+# Edge 1
+circle c1 150 0 0 0 0 1 50
+mkedge e1 c1 0 3.14159265358979323846
+
+# Edge 2
+circle c2 50 0 0 0 0 1 50
+mkedge e2 c2 0 3.14159265358979323846
+
+# Make wire
+wire w e1 e2
+
+# Fillet (analytical)
+fillet2d result w 10
+
+set length 275.293
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A6 b/tests/fillet2d/fillet2d/A6
new file mode 100644 (file)
index 0000000..31ac239
--- /dev/null
@@ -0,0 +1,14 @@
+# Edge 1
+line l1 0 0 0 1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 0 0 0 1 0
+mkedge e2 l2 0 100
+
+# Fillet (analytical)
+fillet2d result e1 e2 10
+
+set length 15.708
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A7 b/tests/fillet2d/fillet2d/A7
new file mode 100644 (file)
index 0000000..7683786
--- /dev/null
@@ -0,0 +1,14 @@
+# Edge 1
+line l1 100 0 0 -1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 0 0 0 1 0
+mkedge e2 l2 0 100
+
+# Fillet (analytical)
+fillet2d result e1 e2 10
+
+set length 15.708
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A8 b/tests/fillet2d/fillet2d/A8
new file mode 100644 (file)
index 0000000..aecf1c6
--- /dev/null
@@ -0,0 +1,14 @@
+# Edge 1
+line l1 100 0 0 -1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 100 0 0 -1 0
+mkedge e2 l2 0 100
+
+# Fillet (analytical)
+fillet2d result e1 e2 10
+
+set length 15.708
+
+set 2dviewer 1
diff --git a/tests/fillet2d/fillet2d/A9 b/tests/fillet2d/fillet2d/A9
new file mode 100644 (file)
index 0000000..fd7eff2
--- /dev/null
@@ -0,0 +1,14 @@
+# Edge 1
+line l1 0 0 0 1 0 0
+mkedge e1 l1 0 100
+
+# Edge 2
+line l2 0 100 0 0 -1 0
+mkedge e2 l2 0 100
+
+# Fillet (analytical)
+fillet2d result e1 e2 10
+
+set length 15.708
+
+set 2dviewer 1
diff --git a/tests/fillet2d/grids.list b/tests/fillet2d/grids.list
new file mode 100644 (file)
index 0000000..821adfc
--- /dev/null
@@ -0,0 +1,3 @@
+001 chamfer2d
+002 fillet2d
+
diff --git a/tests/fillet2d/parse.rules b/tests/fillet2d/parse.rules
new file mode 100644 (file)
index 0000000..eab3eb5
--- /dev/null
@@ -0,0 +1,4 @@
+FAILED /\bFaulty\b/ bad shape
+IGNORE /^Error [23]d = [\d.-]+/ debug output of blend command
+SKIPPED /Error: unsupported locale specification/ locale is unavailable on tested system
+OK /Relative error of mass computation/ message from vprops