Where is the best thread making video featuring correct method?

Hi Helvetosaur,

Three points:
(1) Thanks for researching drill-pipe joints. I wasn’t able to look into it much.
(2) I analyzed your example files, and pretty much agree with your analysis of your specific examples.
(3) I disagree with your final conclusion, though. I think your conclusion is correct for your examples, but I think Sweep1 fails on my test cases. I invite you to re-create the following example from scratch, and see if you get the results I did. If you get different results, then maybe I did something wrong?

Here’s a thread specification, that is vaguely similar to a drill-pipe joint. The thread-pitch is larger than in your example, and the taper is 1:4. The fine grid lines are millimeters, so the major grid is in centimeters. The indicated lengths are in millimeters. In this example, the file’s tolerances are 0.001 mm (1 micron) for distance, and 0.1 degrees for angle. The 1 micron tolerance is the same as in your examples.

Initial radius is 60mm which tapers down to 35mm over a distance of 100mm. Thread pitch is 10mm. The thread-form is a 60-degree triangular profile (green polyline). I drew the thread-profile similarly to how you did in your examples.

Next, we use Rhino’s Curve->Spiral command to make a tapered-helix with the above specifications.

And we follow with a Sweep1-roadlike, where the tapered helix is the sweep-rail, and the green curve is the sweep-shape.

This Sweep1 surface looks okay at a glance, but I had lots of problems with it. Let me show you.

I slice this surface through the XZ-plane, and discard the front half. That leaves us with a bunch of half-thread surfaces. I’ve colored the top two half-threads brown and cyan.

When I to join the top two half-threads, Rhino refuses. Rhino says the two surfaces don’t align. So let’s zoom in.

The selected line-segment (yellow) is across the gap. We see a gap of around 5 microns, which is 5x the size of our tolerance of 1 micron. That’s too huge, so Rhino says it can’t join the surfaces.

I invite you, and anyone, to re-create this example. I would like to know if you also get a gap, and if so, how big is it? And if you don’t get a gap, then what did you do that is different from what I did?

Finally, we compare this to a lathe-cut version, to contrast it with Sweep1-roadlike. Here there is no gap.

So, I would say:
(1) Recreate this example of a tapered thread.
(2) See if you get similar or different results than me. Did I make a mistake?
(3) It can be worth discussing if this example is (a) realistic, and (b) if the error actually matters in practice.

i would suggest that this example is not too different than other thread-forms in photos of drill-pipe joints. But this is debatable.

This error mattered to me, in practice, because it prevented my Grasshopper script from generating a solid that was watertight to within my tolerance of 1 micron.

I think Rhino is doing double-precision floating point operations, so an error of 5 microns / 100 mm = 5e-5. For a simple example like this (no weird numerical instability), one would expect Rhino to achieve relative accuracies of better than 1e-6, if not much better. (In theory, double-precision floating-point, can represent numbers to a relative accuracy of 1e-16.) So, in my humble opinion, there is no reason Rhino shouldn’t get the geometry right to better than 1 micron, in this example.

So 5 micron errors, prevented my Grasshopper script from joining surfaces together. But how big is 5 microns in real-life? I’m not a machinist, but I hear that 0.0002 inches (0.2 mils) is considered very precise for objects of around 12 inches, and accuracies higher than that are very-high-precision. If you convert, 5 microns is just about 0.0002 inches (0.2 mils). Does that matter? I suppose it depends on the application. In drill-pipe joints, it might not?

Even if not, it’s still very annoying for scripting in Grasshopper. So I don’t use Sweep1-roadlike, and instead create the lathe-cut geometry in the way you described. (For me this was very fun and satisfying to figure out in terms of the math.)

Here are all the files for this example.
Sweep1-roadlike vs Lathe.zip (5.3 MB)

–Anthony Yan

1 Like

Use RailRevolve for a tapered thread (or a straight thread) with the section always at the same angle to the helix axis.
RaiRevolve threas.3dm (2.0 MB)


swamped by you all having a very good debate, am I being overlooked ?

please see last post on method for making the female thread please.


1 Like

@Anthony_Yan example using RailRevolve:
RailRevolve AY Example DC01 .3dm (2.1 MB)

1 Like

Hi Steve1,

Here’s a quick description, hopefully still clear:

I’ve tried 2 ways to make female threads:
(1) Applying Offset Surface to the male-thread.
(2) Creating a second “male thread” exactly like the first, except the thread-profile has been offset (Offset Curve) and then trimmed or extended to the thread-pitch.

A couple of notes:

Offset Surface can work, but in my experience sometimes failed. This is because internally, Offset Surface is an insanely complicated command. This is not obvious, but once you look under the hood, you seen a tremendous amount of programming goes into this one command. For the same reason, it can be very slow, too.

I recommend using Offset Curve on the thread profile. Offset Curve by the amount of tolerance you want between the male and female threads (bolt and nut). You can decide if you want the offset-curve to have sharp or rounded corners, etc.

The Offset Curve may have funny start and end segments. It might be slightly too long, or too short, for the thread pitch. So extend and/or trim the Offset Curve so that it matches the thread-pitch of the original curve.

What I do, is make three copies of the thread-profile stacked together. I then offset-curve and trim to the thread-pitch. I do this because the ends off an offset-curve can be a little weird (see areas inside black circles). The offset ends can be “too long” or “too short” depending. By having an extra copy of the thread-profile, we can avoid the offset-ends.

Then create a threaded surface as usual. You can then make it a solid. From the solid, you can do boolean operations to get the gender you want (say, take the offset-thread and subtract it from a plate to make a female threaded hole).

If you look into the thread generator, it actually makes three surfaces internally, and you get to pick which one(s) you want: The original thread-surface, and then based on offset-curve, a slightly larger and a slightly smaller thread-surface. The thread-profiles are offset by +tolerance and -tolerance that you can choose.

Hope that helps… Also ask around to see what other people do too.

–Anthony Yan

1 Like

Thanks for this pointer. I analyzed the example file you gave (which is a modified version of the example file I gave).


Rail-revolve didn’t do exactly what we wanted.

Looking at the documentation, we see that RailRevolve actually re-scales the profile shape in the radial direction.
“The profile curve, in addition to being revolved, stretches along the revolve axis using the revolve axis origin as the scale base point.”

As the threads taper closer and closer to the center axis, they also get squished shallower and shallower. What we want is for the thread profile to be moved rigidly, without changing shape.

So I don’t think RailRevolve will work for tapered threads. But it should be fine for straight threads.

–Anthony Yan

1 Like


I can reproduce what you are seeing with your data. Apparently the finer threads in my example kept any error below the file tolerance, so all joined up correctly. With your coarser threads, I see that the sweep fails to follow the rail and the error gets larger as it moves along towards the top - the error is minimal, but yes, each surface is about 0.005 off at the top of the rail (too far out) and the top surface edge ends up just slightly off the theoretical line through the centerline - by about 1/4 of a degree. And the procedure produces the same error even if I tighten the file tolerance to 0.0001 or even 0.00001 - so it is not paying attention to the tolerance as it should.

It’s too late here to go into a detailed description, but tomorrow I will write up a full report. There is definitely a bug that shouldn’t be there. I have a workaround or two perhaps. Thanks for keeping at it.


I created an official youtrack bug report here:


This is the file used:
TaperedThread-Sweep1Problem.3dm (4.8 MB)

Generally, to ‘force’ a correct ending of a Sweep1, I often put a second profile at the end of the rail. This unfortunately doesn’t work with the V-Profiles in the example, as Rhino seems to get confused - perhaps because both ends of the profile are on the same rail.

The workaround in this case is to sweep the two straight “legs” of the V-profile separately using a start and end profile. This keeps the surfaces on the rails and oriented correctly (as far as I can tell). they can then be joined within tolerance.

There are certainly scripted/GH solutions using many oriented profiles and something like Loft that might also work.

1 Like

I have three general questions:
(1) For Sweep1-roadlike, I’m not actually sure this is a bug. Based on the analysis I’ve been trying to explain (badly?), I think Sweep1-roadlike is working as intended.
(2) There’s a feature in Sweep1-Roadlike called “Set Axis” which I don’t understand right now. Anyone able to explain it? I think I can figure it out, but am confused at the moment.
(3) I wonder if Rhino3d should add a new command that does a Sweep for tapered threads?

My overall understanding (based on the math I did):
(A) Sweep1-roadlike uses the TNB frames to orient sweep-shapes.
(B) The TNB frames don’t do what you think they do, for tapered-helices. This bit is math. (But for straight helices, they behave intuitively.)

Therefore, Sweep1-roadlike doesn’t do what you think it should do, on tapered helices.

(C) Because of (A) and (B), I wrote my own script to generate tapered threads. And it does what you might think: Create a bunch of sweep-shapes that are oriented correctly and pointing towards the helix-axis, then uses Loft to generate the surface.

The most unintuitive part is (B), I think. Or at least it was for me, initially.

1 Like

The “roadlike” designation was inherited from versions 5 and earlier where only Top, Front and Right were available. In V6 onwards one could set an axis which will be the Z-axis relative to which the planes should be oriented. This is different than “freeform” which allows the frames to be oriented around the curve as the curve math dictates - I thought that this is what TNB represents. In “roadlike”, the Z axis of all the frames is supposed to be made to point in the same direction as the set axis - i.e the normal curve perpendicular frames are all transformed. At least that’s how I understood it.

In any case, yes, there is the case of the frames not being oriented along the axis - slightly in this case, but still there - but there is also the out-of-tolerance deviation of the resulting surface edge from the rail curve, which IMO is not supposed to happen.

1 Like

When I’ve been using that command to make helices for threads, I’ve found that I’m obligated to use ‘set axis’ so I don’t have to keep orienting the Cplane z-axis. It’s easier for me to ‘set the axis’ manually on the fly every time. It’s annoying though, but slightly less annoying than keeping track of the cplane and managing and maintaining it’s orientation etc.

Although it seems the cplane orientation approach might be better if you have-to helices multiple-entities in the same orientation. So, I guess I juggle between whatever works. It’s just annoying when the cplane seems correct, but the sweep fails without ‘setting the axis’ regardless.

The last thread I created here, was one where I helice’ed like six entities and then joined them to get the edge/iso composition I wanted. It was annoying having to ‘set axis’ six times in a row, but it had to be done, so…

In the future, I’ll try a different approach where I pay more attention to the cplane, but I’m not sure it’s 100% guaranteed workflow. ‘Set axis’ seems like more of a guarantee, but also redundant steps every time since it’s not ‘stored’ or something.

Ikr, this ‘spiral’ approach I’ve been seeing is weird cause, I though ‘helix’ should handle tapers idk.

I haven’t done much tapered threads in Rhino, so idk if ‘helix’ even has the option, or if you have to use ‘spiral’ or something… :sweat_smile: lol yep I just read the help docs and ‘spiral’ and ‘helix’ are funny imo :smiling_face_with_tear: R8 should just merge them or something :stuck_out_tongue_closed_eyes: – imo :grin:

Jus going to put them side by side, brb:

hmmmm :face_with_monocle: :thinking: :thought_balloon: sooo, a spiral is a flat 2D shape, buut in Rhino you can make it 3D so then a spiral is a tapered helix, but a helix can’t taper… k I think I got it. :joy: :beers:

1 Like

Two things:
(1) I’m going to submit a feature request. As David Cockey noted, RailRevolve almost does the right thing. If we could somehow turn-off re-scaling, RailRevolve could be used to make tapered threads. So maybe Rhino3d could get an extra checkbox that says,“No rescaling,” or “Do not rescale” in the RailRevolve dialog window?

(2) Here is one last example (for now), that shows Sweep1-roadlike’s problem is not a bug. Or at least attempts to show this.

One possible “bug” is numerical instability. That is, hypothetically, Rhino is attempting to do the correct thing, but the algorithm doesn’t have enough numerical accuracy.

However, if the error is huge on a simple case, then we should either be able to explain the huge error (by analyzing the numerical stability), or the “error” isn’t actually a bug.

So this example uses a severe-taper to make a huge “error.” The taper is severe in that the taper-angle is huge (approaching 45 deg) and the threads spiral inwards until they are within one thread-pitch of the central axis. Realistically you would never having threads that spiral so close to the central axis. But it’s only in these cases that the effect gets big. (As Helvetosaur notes, in many common cases the taper is not severe, and the effect can be below any reasonable tolerance. For example, NPT threads have a small taper.)

Since the example shows a very noticeable error, we have to conclude that either there is a numerical-instability with Rhino’s algorithm, or we have to conclude that Sweep1-roadlike is correct and accurate, but that the geometry is not intuitive.

For mathematical reasons, I’m almost sure Sweep1-roadlike is correct, but doing non-intuitive things (see discussion about TNB frames, link to wikipedia, and/or any vector-calculus textbook).

Here is a straight helix.

Now we add a tapered helix, that has exactly the same thread-pitch. If you like, we are projecting the straight helix onto a cone, and our lines of projection are directly onto the Z-axis.

Finally, we project our tapered helix straight down onto the XY-plane.

We can now do a straight loft between these curves. These two surfaces are exactly what you think they are, intuitively. The first surface has iso-params that are perfectly radial; if you extended them, they would intersect the Z-axis perpendicularly. And they would be parallel to the XY-plane. The second surface has iso-params that are parallel to the Z-axis. Both surfaces can be generated by sweeping straight lines, in the way you would expect. These surfaces are a visual reference for axial and radial directions.

Next, we set up a local “XYZ-axis” at the start of the tapered helix. We start these local XYZ-axes square to the world XYZ-axes (just in a different order). Those are the red, green, and blue line segments on the lower right.

Next, we apply Sweep1-roadlike three times, once for each of our local axes. That generates the red, green, and blue surfaces. Notice the direction of the surfaces, as well as their iso-params.

As you can see, the colored surfaces are not aligned with our original two grey surfaces. Their iso-params are not aligned. And the actual geometry (independent of their parameterizations) are not aligned.

In particular, look at the final orientation of the local XYZ axes (final edges of the red, green, and blue surfaces). Clearly the final orientation is not square to the world Z-axis. And the deviation is quite noticeable (ie: “huge”), especially in the Top view (looking downwards along the Z-axis onto the XY-plane).

So the deviation can be surprisingly huge, for extreme tapers.
And the deviation can be surprisingly tiny, for small tapers.
I think this deviation is actually correct, and due to how Sweep1-roadlike uses TNB-frames to align the rail-shapes.

All of the files for these diagrams are here:
Sweep1 Tapered Helix Example 2.zip (11.6 MB)

–Anthony Yan

1 Like


Sorry for the delay and thank you for typing the details. I added a brief description about pre-select. I’m not going to add more details because that may not be understandable to most users.

  1. Select the cutting objects.
    If the cutting objects are also to be trimmed, you can preselect all the objects before starting the command.
  2. Select the parts of other objects or the cutting objects themselves to trim away.

And replaced the topic image with a GIF that shows post-select and pre-select.

Yeah that GIF is confusing. The final trim selection doesn’t make sense. Guess I’d have to understand the part. Almost looks like a lego or something :slightly_smiling_face:

I’m familiar with the ‘pre-select’, but I don’t use it all the time, just sometimes – well maybe 50/50 or 40/60.

Hmm… I should’ve added some descriptions.
Only the surface is selected as the cutting object. The pipes won’t trim the surface.
All objects are selected as cutting objects. The surface and pipes can trim each other.

1 Like

This second gif is much better because your previous gif gave the incorrect impression that the objects that had been pre-selected and trimmed are left un-selected at the end of the process. It is important that users who are interested in using this workflow understand that after the trim command ends all the objects will remain selected. That is important because typically the next step in this workflow is to hit the Join button.

Its unfortunate that you think users won’t be able to understand if you inform them that this workflow of trimming and joining is similar to what a boolean does but is more accurate and more reliable way of doing it. What I think users won’t be able to understand
is that if this method of trimming and joining is generally more accurate and more reliable than doing a boolean to get a similar result, then why is McNeel withholding that information.

1 Like

You really see all the details. :slightly_smiling_face:

The GIF is a combination of many static images including the mouse cursor. It may be inconsistent with the real Rhino UI. For example, in the real Rhino UI, when you click on the part of the pipe to trim, the part just disappears, and the “busy” circular icon appears while Rhino is remeshing the trimmed pipe.


I’d like to show the part of the pipe gets selected and then trimmed (disappears), and eliminate the annoying “busy” circular icon, and have a smooth and accurate mouse movement…


I’m sorry. I tried the model in your post hoping to understand what you had said. Honestly, with 20 years of experience of using Rhino, I failed.

1 Like

Well, if I understood correctly, @jim claims that by Trimming and Joining you get more accurate results than something like a Boolean operation.

I tried testing that with a file similar to the example, 4 cylinders and a degree 3 6 point surface derived from the extrusion of a curve.

I then made a copy of the objects before trimming, then did the Trim/Join in one go on the left hand ones and a BooleanDifference on the right hand one.

Here are the results of the edge tolerances:


  Edge Tolerances: 0.00000000 to 0.00047531
    median = 0.00000000 average = 0.00015844
  Vertex Tolerances: all 0.00000000

  Edge Tolerances: 0.00000000 to 0.00048965
    median = 0.00000000 average = 0.00016322
  Vertex Tolerances: all 0.00000000

  Edge Tolerances: 0.00000000 to 0.00048965
    median = 0.00000000 average = 0.00016322
  Vertex Tolerances: all 0.00000000

  Edge Tolerances: 0.00000000 to 0.00047531
    median = 0.00000000 average = 0.00015844
  Vertex Tolerances: all 0.00000000


  Edge Tolerances: 0.00000000 to 0.00049219
    median = 0.00000000 average = 0.00016406
  Vertex Tolerances: all 0.00000000

  Edge Tolerances: 0.00000000 to 0.00049219
    median = 0.00000000 average = 0.00016406
  Vertex Tolerances: all 0.00000000

  Edge Tolerances: 0.00000000 to 0.00042499
    median = 0.00000000 average = 0.00014166
  Vertex Tolerances: all 0.00000000

  Edge Tolerances: 0.00000000 to 0.00042499
    median = 0.00000000 average = 0.00014166
  Vertex Tolerances: all 0.00000000

I don’t see a huge difference between the two. But this is obviously just a simple example. I can’t speak for other cases.

TJ-BD-Test.3dm (2.4 MB)


I guess I was mostly just confused as to why the compound surface would be trimmed to remain hidden inside those cylinders – at all.

Visually it would make more sense for me to see the compound surface to be trimmed from inside the cylinders and thereby remain outside and around them.

I’m looking at the object and just trying to make sense of it is all, maybe I’m just being too critical :sweat_smile:

Yes, very good point. I’ve noticed some colleagues do that and don’t understand nuances like that for sure :sweat_smile:

Like how hard is it to get them to read the command prompt too though :sweat_smile:

1 Like

Yes. It’s not about the object in this case, it’s about the process.

1 Like