need help to understand pcurve / helix


I am working on a path display and am currently stuck on the implementation of Helix. I tried to understand MakeBottle.cxx from the occ-examples, but I had no chance. I understand too little about OCC to distinguish between Helix and the surfaces for the threading.

Fortunately, I discovered a post on cppblog that deals with Helix and OCC. I ran the Chinese through the translator, but somehow I'm still stuck.

So that a helix is a straight line on a cylinder surface is kind of clear. However, I don't understand all the steps to create the helix (from the blog).

The straight line "aLine2d" seems to be a vector. From the center axis of the cylinder to the shell surface with radius as one direction and cylinder axis as second direction. So far so good.

GCE2d_MakeSegment seems to be responsible for creating the helix. Could someone please help me understand what exactly the function does? Or point me to some background information? Maybe there is some prose to read? If possible written for dummies - i.e. without a PhD in math.

From the path library I use, I get three 3D space coordinates (start, end and center), the number of full turnarounds and the vector of the cylinder center axis.

I would like to understand GCE2d_MakeSegment to the extent that I can adapt the code to my needs.

Roman Lygin's picture
DjangoReinhard's picture

Hi Roman,

thank you for your attention! I already found your blog before - so unfortunately no. I looked at ShapeExtend_ComplexCurve and tried to follow your code excerpts - but the more I ask auntie google, the more I felt lost in space.

May be I'm too simpleminded for occ ...

So please let's go back to my starting point. helix creation from blog seems to be straight forward - although the sample is almoust senseless ...

  Handle(Geom_CylindricalSurface) cyl  = new Geom_CylindricalSurface(gp::XOY(), radius);
  gp_Lin2d                        aLine2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(radius, pitch));
  Handle(Geom2d_TrimmedCurve)     seg = GCE2d_MakeSegment(aLine2d, 0, 2.0 * M_PI).Value();
  TopoDS_Edge                     aHelixEdge = BRepBuilderAPI_MakeEdge(seg
                                                                     , cyl
                                                                     , 0
                                                                     , 2 * M_PI).Edge()

I can only guess, what each function does. I suppose, that GCE2d_MakeSegment() creates a number of lines based on line-vector and value-range. BRepBuilderAPI_MakeEdge() then wraps these lines around the cylindrical surface.

I played with 3rd and 4th parameter - but did not get the point. When 3rd parameter is greater 0, than the helix starts at different height. So I guess, a positive value determines, how many segments from starting point are ignored.

4th parameter caused a lot of headache. First I thought (based on pi usage), that parameter would specify the angle of turnarounds. But this is not true. 2Pi does not lead to a (projected) full circle. Then I found out, that length of helix does depend on pitch value. So I thought, 4th parameter may lead to wirelength of helix, but that isn't true neither.

Is there a way to solve a helix this way? May be some of you could shine me a light or gimme a helping hand ...

actually I'm at this point:

Handle(AIS_Shape) makeCylHelix(const gp_Point& from
                             , const gp_Point& to
                             , const gp_Point& center
                             , const gp_Dir&   axis
                             , int             fullTurns) {
  gp_Lin centerAxis(center, axis);
  gp_Ax2 cylAxis(center, axis);
  gp_Lin normStart = centerAxis.Normal(from);
  gp_Lin normEnd   = centerAxis.Normal(to);
  double r0        = centerAxis.Distance(from);
  double r1        = centerAxis.Distance(to);
  double angle     = normStart.Angle(normEnd);
  double height    = normStart.Distance(normEnd);
  double pitch     = height / ((fullTurns * 2 * M_PI + angle) / 2 * M_PI);

  TopoDS_Shape aCyl = BRepPrimAPI_MakeCylinder(cylAxes, r0, height).Shape();

  Handle(Geom_CylindricalSurface) cyl = ???

  gp_Lin2d                    helixLine(center, gp_Dir2d(r0, pitch));
  Handle(Geom2d_TrimmedCurve) segs = GCE2d_MakeSegment(helixLine, 0, 2.0 * M_PI).Value();
  TopoDS_Edge                 aHelixEdge = BRepBuilderAPI_MakeEdge(segs
                                                                 , cyl
                                                                 , 0
                                                                 , ??? * 2 * M_PI).Edge();

  return new AIS_Shape(aHelixEdge);

I didn't find a way (yet) to create a Geom_CylindricalSurface from TopoDS_Shape. Then I guess, I have to apply some rotation and moves to the helixEdge to get the helix fit the points. How can I change the starting point of the helixEdge (in rotation sense respect to cylinders center)?

Sergey Slyadnev's picture

At the first glance, this 2D segment is a parametric curve that lives in the UV space of a cylinder. Once mapped back to 3D, it becomes a helix. The magic here is this UV -> XYZ mapping. If you wish to elaborate on that matter, you can email me by sergey.slyadnev [at] (I promise not to take your money :)

DjangoReinhard's picture


I tried to use the code from your blog. Lots of changes where required to make it compilable. I always have a bad feeling, modifying code, I don't understand ... ... so it happens, what I expected: application crashes.

Exception: Standard not implemented at FirstParameter()

Although I don't understand, what's going on, I think a wrapper like a Handle-class should implement the entire public interface of the class it wraps. Or have a mechanism, that routes Handle-calls to the wrapped class. I don't see such mechanism, so I guess, I have to implement the public interface in the handle class.

Some help/comments would be very appreciated!

I attach the changed code, that does not work, but at least is compilable (with occ from git master).

Adding MakeHelix to Geom_HelixData is far from elegant, but at the moment I only care for getting it to work. I use the Helix stuff like this:

    Handle(Geom_BSplineCurve) aHelix;
    ACISGeom_HelixData helDat;
    gp_Ax3 pos;

    helDat.setScaleFactor(3 * M_PI);    // 1.5 turns

    if (helDat.MakeHelix(helDat, aHelix)) {
       TopoDS_Shape ts = BRepBuilderAPI_MakeEdge(aHelix);

       myContext->Display(new AIS_Shape(ts), AIS_WireFrame, 0, false);
DjangoReinhard's picture


I extended the handle class and it seems to work so far.

Creation of the helix is called like this:

  gp_Pnt points[] = { gp_Pnt(5, -29, 2)
                    , gp_Pnt(-15, -9, 2)
                    , gp_Pnt(13.284, -9, 0)
                    , gp_Pnt(-6.716, -29, 0) };
  gp_Pnt center(-0.858, 5.142, 0);
  gp_Dir cAxis(0, 0, 1);

  path.append(makeLine(points[0], points[1]));
                      , points[2]
                      , center
                      , cAxis
                      , false
                      , 0));
  path.append(makeLine(points[2], points[3]));

makeHelix is a wrapper around the MakeHelix from blog like this:

  Handle(AIS_Shape) OccHelper::makeHelix(const gp_Pnt& from
                                       , const gp_Pnt& to
                                       , const gp_Pnt& center
                                       , const gp_Dir& axis
                                       , bool          ccw
                                       , int           fullTurns) {
  Handle(Geom_BSplineCurve) aHelix;
  Geom_HelixData helDat;
  gp_Ax3 pos(center, axis);
  gp_Lin centerAxis(center, axis);
  gp_Ax2 cylAxis(center, axis);
  gp_Lin normStart  = centerAxis.Normal(from);
  gp_Lin normEnd    = centerAxis.Normal(to);
  double r0         = centerAxis.Distance(from);
  double r1         = centerAxis.Distance(to);
  double angStart   = pos.XDirection().Angle(normStart.Direction());
  double angEnd     = pos.XDirection().Angle(normEnd.Direction());
  double angle      = 2 * M_PI - normStart.Angle(normEnd);
  double height     = normStart.Distance(normEnd);
  double turnFactor = (angle + (fullTurns * 2.0 * M_PI)) / (2.0 * M_PI);
  double pitch      = height / turnFactor;

  qDebug() << "angle of start point:" << angStart
           << "angle of end point:" << angEnd;
  qDebug() << "makeHelix: r0=" << r0 << "r1=" << r1
           << "height=" << height << "angle=" << angle
           << "pitch=" << pitch << "turnFactor=" << turnFactor;
  qDebug() << "pitch * turnFactor = " << (pitch * turnFactor);

  if (ccw) pos.XReverse();
  Handle(AIS_Shape) rv;

  if (helDat.MakeHelix(helDat, aHelix)) {
     TopoDS_Shape ts = BRepBuilderAPI_MakeEdge(aHelix);
     gp_Trsf rot;

     rot.SetRotation(pos.Axis(), -angEnd);
     TopoDS_Shape rts = BRepBuilderAPI_Transform(ts, rot);

     rv = new AIS_Shape(rts);
  return rv;

I stil have a few questions, as the sample only works on trial and error.

  • where do I know from, whether the given angle is smaller or bigger than PI

  • where do I know from, whether I have to rotate the helix by angStart or angEnd and whether I have to use the original angle or its negative counterpart

DjangoReinhard's picture


Would it be possible to get help by paid support?

If so, could you please contact me by email and send me a favorable offer?

Vera Sdobnova's picture

Hello DjangoReinhard,

Information on official OCCT technical support can be found via the link.
For more details, please contact Open Cascade via the contact form.


Roman Lygin's picture


Thank you for your interest but sorry, no bandwidth for any personal consulting while managing a company.
As a personal advice - refreshing your knowledge (or some learning) in computational geometry or math analysis should definitively help to figure the thing out. And folks at OCC will be happy to take your money.
Good luck!