Offsetting curves by incremental distance

New to Grasshopper.
Trying to offset a curve multiple times (up to 50 times) while increasing the offset by 10% each time.

  1. offset by 1 mm
  2. Offset by 1.1 mm
  3. Offset by 1.21 mm
  4. offset by 1.33 mm
  5. Offset by 1.46 mm
    I tried using the series function but keep getting stuck as it uses the addition not multiplication.
    Would appreciate any guidance.
    Thank you.

Anemone loop? (9.3 KB)

1 Like

Thank you so much, took me time to figure out how to install the plugin.
The result is just what I was looking for.
Appreciate the help

Anemone is a wonderful tool to know, very versatile. Here’s the same thing in Python: (5.8 KB)

res = []
sa = startAt

for i in range(reps):
    sa = sa * incr

P.S. The Python version includes ‘startAt’ in the results, Anemone version does not. Either one could be modified slightly to match the other, depending on which you want?

The Anemone version results includes ‘-1’, which is surely not wanted, but it’s a little harder to avoid. Python version is cleaner and shorter. Both tools are handy, for many things.

Forgive me but is Python another plugin?
If so, where can I add this wonderful tool?
I work on a PC.

Thank you!

Here is a version in c#, shouldn’t require any plugin. (7.9 KB)

1 Like

Python is built into R6, a plugin for R5:

Python version is much simpler than C# version! (below) You decide.

using Rhino;
using Rhino.Geometry;
using Rhino.DocObjects;
using Rhino.Collections;

using GH_IO;
using GH_IO.Serialization;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Runtime.InteropServices;

/// <summary>
/// This class will be instantiated on demand by the Script component.
/// </summary>
public class Script_Instance : GH_ScriptInstance
#region Utility functions
  /// <summary>Print a String to the [Out] Parameter of the Script component.</summary>
  /// <param name="text">String to print.</param>
  private void Print(string text) { /* Implementation hidden. */ }
  /// <summary>Print a formatted String to the [Out] Parameter of the Script component.</summary>
  /// <param name="format">String format.</param>
  /// <param name="args">Formatting parameters.</param>
  private void Print(string format, params object[] args) { /* Implementation hidden. */ }
  /// <summary>Print useful information about an object instance to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj) { /* Implementation hidden. */ }
  /// <summary>Print the signatures of all the overloads of a specific method to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj, string method_name) { /* Implementation hidden. */ }

#region Members
  /// <summary>Gets the current Rhino document.</summary>
  private readonly RhinoDoc RhinoDocument;
  /// <summary>Gets the Grasshopper document that owns this script.</summary>
  private readonly GH_Document GrasshopperDocument;
  /// <summary>Gets the Grasshopper script component that owns this script.</summary>
  private readonly IGH_Component Component;
  /// <summary>
  /// Gets the current iteration count. The first call to RunScript() is associated with Iteration==0.
  /// Any subsequent call within the same solution will increment the Iteration count.
  /// </summary>
  private readonly int Iteration;

  /// <summary>
  /// This procedure contains the user code. Input parameters are provided as regular arguments,
  /// Output parameters as ref arguments. You don't have to assign output parameters,
  /// they will have a default value.
  /// </summary>
  private void RunScript(double s, double p, int c, ref object A)
    List<double> result = new List<double>();
    c = c - 1;
    for (int i = 0; i < c; i++)
      s = s * p;
    A = result;

  // <Custom additional code> 

  // </Custom additional code> 
1 Like

Python version is much simpler than C# version!

and much slower :stuck_out_tongue_winking_eye:

Don’t know, don’t care, works for me. (your words from a different thread)

Speed makes no difference at all for many things, like this simple loop.

like this simple loop.

Until that loop gets big

Thanks for taking the time. These solutions are so simple yet so powerful!

You can use this solution of David (8.0 KB)


Aren’t we making this more complex than what it is?

Anyway, @Joseph_Oster i think your first answer might be the best one in some situations.
As the original question was about offsets (so not just a pure math riddle), as we know rhino, the offset of an offset (of an offset…) might be different from a single offset with the same total distance.
For examples, rhino, with big values and self-intersecting theoretical offset results, often fails… by doing “smaller steps” (anemone) one can reach the correct solution.


In this case the best is probably using those values with Clipper.

With the risk of going a little off-thread …
I’ve never downloaded clipper, as I generally like to avoid plug-ins (working on many different machines, restarting rhino everytime is a problem, expecially with big files…).
I’ll surely download and use it if i’ll find myself in the situation where is strictly needed.

But… shouldn’t rhino/grasshopper “deserve” a proper working offset function?
Out of all the plug-ins out there, clipper seems like the most absurd to need.
We are not talking about a complex specific function, but just the offset.
Usually it is: plug-in for a new function.
Clipper is: plug-in to fix a function.

… can’t McNeel “buy” it and integrate it? :sweat_smile:

I’ve never downloaded clipper, as I generally like to avoid plug-ins

Fair enough, but Anemone is a Plug-in as well (of course one of my favorite)

can’t McNeel “buy” it and integrate it?

I am not sure what the protocol is for that, it is an open source freeware library (

Good point… well, it seems I’m not able to overcome the fact that I need a plug-in to fix a function… I usually prefer to fix my definitions until the normal offset just work all the times (ex by simplifying the logic).

Well, this make the situation even more absurd. Offset on rhino works the same (sometime bad) since… years?
Who know what are the events during the development of a software … not me!

1 Like (11.2 KB)

1 Like

I know it’s been discussed a lot before, but there might be some problems that are hard to solve. In fact, quite a few people expect to improve the Offset Curvefunctionality.:expressionless:
BTW, regarding clipper, it’s superior functionally as oppose to native Offset Curve, but the problem is limited to polygon or polyline offset. The result is always polyline. On the other hand, the GH’s case is more a broader curve in general.

but the problem is limited to polygon or polyline offset.

I typically rebuild the polygon result via the Kinky Curve component which seems to work well, keeping kinks at expected places and interpolating curves at others.

1 Like