Video : Offset Curve Dave Hack

Learn a great ‘hack-nique’ for off-setting clean curves with equally clean new curves, as opposed to the usual complicated mess. These cleaner curves will make a huge difference in creating higher quality organic surfaces. Running time = 04:36.

More cool and free videos at


This would be a great one to have scripted.

Hi Dave
This is indeed very useful – never thought about using UVN this way.
One caveat has to be mentioned though:
While the “loose” option of “offsetSrf” usually is within reasonable tolerance of the desired offset, Your method may deviate quite significantly from the value used in the UVN transform.
It can be a very useful hack though, if you remember keep an eye on the effective offset of the curve.

Cheers and thank You, Norbert

That is a very usefull trick, is this an exact offset or is it an estimate?

What degree was the base curve youre working with and would this work with just any curve (degree)?

It’s not an exact offset - the reason why Rhino adds control points is that it needs to do so to create an offset within tolerance. However, in situations where the quality of the curve is more important than the exactitude of the offset, this is handy. Always check the curve min and max deviation after.


Yeah, thats what i was wondering and thats why i asked about the degrees because i think this works better on degree 2 curves than anything else,…not sure,…especially when going from positive to negative with larger and smaller radii,…

Understood. That’s why I showed the curve offset command first, using the file tolerance. It’s accurate, but not what I want or need.

Its an estimate. The trade off of losing accuracy is that you get an exact number of control points, which is far better than doing it manually, point-by-point. [That’s actually how I developed the technique.] If you’re modelling an organic character, no one will ever see (or care!) about the resulting deviation.

If you have a degree 1 or 2 curve, you’re dealing with straight sections and arcs, so the standard curve offset command offset is usually effective and my technique is not needed. I didn’t mention this curve was degree 3, but it should be assumed since the focus is on organic curve and surface modelling.

Here’s the first project where I developed the ‘Dave Hack.’ It’s actually on the market now at amazon.

1 Like

I think the “loose option” of OffsetSrf does a little more with the control points than moving them in the normal direction, that means they are transformed in UV direction too.
That way a closer tolerance to the desired offset value can be obtained.
Maybe someone from McNeel can tell us a little bit more about what exactly happens when “loose” is selected?

Anyway, another reason to have “loose” in the Offset command!


The ‘loose’ option for surfaces was part of the inspiration in developing this curve hack. However, that does not always yield a one-for-one control point result.

Another one of my reasons for making the video and sharing this is my hope that McNeel include a new option in the curve offset command and call it ‘same points’ or ‘Dave Flavor.’


I have a Macro I sometimes use that I named OffsetControlpoints.
it gives a similar (if not the same) result:

!_ExtractControlPolygon _Pause
_Offset Corner=Sharp  _Pause _Pause _Pause
_CurveThroughPolyline _Pause



@Willem - offsetting EditPoints may make a more accurate offset than moving control points. Not as Macro-able though it looks like.


1 Like

great trick!


I tried to convert your Macro to PythonScript just for fun:

-_RunPythonScript (
from Rhino import *
from Rhino.Input import *
from Rhino import *
from Rhino.DocObjects import *
from Rhino.Geometry import *
import math

def test():
  go = Input.Custom.GetObject()
  go.SetCommandPrompt("Select Curve")
  go.GeometryFilter = DocObjects.ObjectType.Curve;
  if go.CommandResult()!=Commands.Result.Success:
	return go.CommandResult()
  objrefs = go.Objects()
  if not objrefs: return Commands.Result.Nothing
  for i, objref in enumerate(objrefs):
		# get the curve geometry
		curve = objref.Curve().ToNurbsCurve()
		(res, plane) = curve.TryGetPlane()
		(res, con1 ) = curve.GetNextDiscontinuity( Continuity.C0_continuous, curve.Domain.Min , curve.Domain.Max)
		(res, con2 ) = curve.GetNextDiscontinuity( Continuity.C0_continuous, 0.1, curve.Domain.Max)
		seamMoved = False
		if con1 <= curve.Domain.Min and curve.IsClosed:
			if curve.ChangeClosedCurveSeam(con1 + con2):
				seamMoved = True
		cp = curve.Points.ControlPolygon()
		cpNurbs = cp.ToNurbsCurve()
		curves = cpNurbs.Offset(plane.PointAt(1000,1000), plane.ZAxis, 2.0, RhinoDoc.ActiveDoc.ModelAbsoluteTolerance, CurveOffsetCornerStyle.Sharp)
		# Use this to flip direction
		#curves = cpNurbs.Offset(plane.PointAt(0,0), plane.ZAxis, 2.0, RhinoDoc.ActiveDoc.ModelAbsoluteTolerance, CurveOffsetCornerStyle.Sharp)
		joinCurves = Curve.JoinCurves(curves, 0.1)
		maxLength = 0;        
		joinedCurve = joinCurves[maxLength].ToNurbsCurve()
		(res, newCP) = joinedCurve.TryGetPolyline()
		mul = 0.5;
		while newCP.Count > cp.Count:
			newCP.ReduceSegments(RhinoDoc.ActiveDoc.ModelAbsoluteTolerance * mul)
			mul = mul + .5
		#RhinoApp.WriteLine( str(newCP.Count) + ":" + str(cp.Count))
		for j, p in enumerate(curve.Points):
			param1 = cp.ClosestParameter(curve.Points[j].Location)
			normParam1 = param1 / cp.Count
			curve.Points[j] = ControlPoint(newCP.PointAt(math.floor(normParam1 * newCP.Count)) , curve.Points[j].Weight)
		#newRef = RhinoDoc.ActiveDoc.Objects.AddPolyline(cp)
		#newRef = RhinoDoc.ActiveDoc.Objects.AddPolyline(newCP)
		RhinoDoc.ActiveDoc.Objects.Select(objref, False)
		newRef = RhinoDoc.ActiveDoc.Objects.AddCurve(curve)
		RhinoDoc.ActiveDoc.Objects.Select(newRef, True)


It is not 100%, but maybe someone else can run with it.

1 Like

Pretty cool. Tried this technique on a simple Rhino ellipse. The results are not good; the fix was to move the outer control points of the offset ellipse to bring the shape back to match the original. Give it a try…cheers, Rob

The technique is designed for organic & degree 3 curves. An ellipse is degree 2, has sharp points, so you would use the standard curve offset command.

A situation where it WOULD work is if you build a ellipse with the deformable (degree 3 ) option OR, use edit→rebuild to convert the ellipse from degree 2 to degree 3. Then it will work swell, gosh darn it.

1 Like

most groovy Obewan (spl?) But in the end, I know too well how important it is to create surfaces from curves that have the least amount of control points and are also quite fair. Thanks for your input and look forward to seeing more of your offerings on the blog. Cheers, Rob

This is another great video, good stuff!

Unf doesn’t work with random curves

You have offset to the point of self-intersection. So, hey, try smaller numbers.

Also, if the curve was originally built from separate pieces (and then joined) there may be seams. This can cause any offset to flip sides. You can either delete the joined control points or use edit–rebuild to guarantee the curve is all degree three.

What is a ‘UNF’? Curious! :cat: