My kingdom for a good curve FILLET module

I have spent all day trying to get a module to fillet a series of curves. None will fillet all the curves. Some will fillet some, others a different group of curves, one most except some curves in the middle… It seems to be totally random.

With this much trouble filleting curves I only spent a few minutes trying to fillet a series of surfaces and gave up on that.

I have formatted the curves very very carefully two adjacent curves that are very similar in contour, one will be filleted, the other not.

FilletCurves (3.0 KB)

Assuming that at least one of these modules is build on this method

Has anyone coded any of these solutions:

In searching I found some C# code.

Perhaps someone could help me implement this code

/// <summary>
/// Round polygon corners
/// </summary>
/// <param name="points">Vertices array</param>
/// <param name="radius">Round radius</param>
/// <returns></returns>
static public GraphicsPath RoundCorners(PointF[] points, float radius) {
    GraphicsPath retval = new GraphicsPath();
    if (points.Length < 3) {
        throw new ArgumentException();
    rects = new RectangleF[points.Length];
    PointF pt1, pt2;
    //Vectors for polygon sides and normal vectors
    Vector v1, v2, n1 = new Vector(), n2 = new Vector();
    //Rectangle that bounds arc
    SizeF size = new SizeF(2 * radius, 2 * radius);
    //Arc center
    PointF center = new PointF();

    for (int i = 0; i < points.Length; i++) {
        pt1 = points[i];//First vertex
        pt2 = points[i == points.Length - 1 ? 0 : i + 1];//Second vertex
        v1 = new Vector(pt2.X, pt2.Y) - new Vector(pt1.X, pt1.Y);//One vector
        pt2 = points[i == 0 ? points.Length - 1 : i - 1];//Third vertex
        v2 = new Vector(pt2.X, pt2.Y) - new Vector(pt1.X, pt1.Y);//Second vector
        //Angle between vectors
        float sweepangle = (float)Vector.AngleBetween(v1, v2);
        //Direction for normal vectors
        if (sweepangle < 0) { 
            n1 = new Vector(v1.Y, -v1.X);
            n2 = new Vector(-v2.Y, v2.X);
        else {
            n1 = new Vector(-v1.Y, v1.X);
            n2 = new Vector(v2.Y, -v2.X);

        n1.Normalize(); n2.Normalize();
        n1 *= radius; n2 *= radius;
        /// Points for lines which intersect in the arc center
        PointF pt = points[i];
        pt1 = new PointF((float)(pt.X + n1.X), (float)(pt.Y + n1.Y));
        pt2 = new PointF((float)(pt.X + n2.X), (float)(pt.Y + n2.Y));
        double m1 = v1.Y / v1.X, m2 = v2.Y / v2.X;
        //Arc center
        if (v1.X == 0) {// first line is parallel OY
            center.X = pt1.X;
            center.Y = (float)(m2 * (pt1.X - pt2.X) + pt2.Y);
        else if (v1.Y == 0) {// first line is parallel OX
            center.X = (float)((pt1.Y - pt2.Y) / m2 + pt2.X);
            center.Y = pt1.Y;
        else if (v2.X == 0) {// second line is parallel OY
            center.X = pt2.X;
            center.Y = (float)(m1 * (pt2.X - pt1.X) + pt1.Y);
        else if (v2.Y == 0) {//second line is parallel OX
            center.X = (float)((pt2.Y - pt1.Y) / m1 + pt1.X);
            center.Y = pt2.Y;
        else {
            center.X = (float)((pt2.Y - pt1.Y + m1 * pt1.X - m2 * pt2.X) / (m1 - m2));
            center.Y = (float)(pt1.Y + m1 * (center.X - pt1.X));
        rects[i] = new RectangleF(center.X - 2, center.Y - 2, 4, 4);
        //Tangent points on polygon sides
        n1.Negate(); n2.Negate();
        pt1 = new PointF((float)(center.X + n1.X), (float)(center.Y + n1.Y));
        pt2 = new PointF((float)(center.X + n2.X), (float)(center.Y + n2.Y));
        //Rectangle that bounds tangent arc
        RectangleF rect = new RectangleF(new PointF(center.X - radius, center.Y - radius), size);
        sweepangle = (float)Vector.AngleBetween(n2, n1);
        retval.AddArc(rect, (float)Vector.AngleBetween(new Vector(1, 0), n2), sweepangle);
    return retval;

I don’t see much difference between this code and that ?

The code above make arcs between 2 half line (infinite) and then add segment between end and beginning of the arcs.

What is the added value of that compared to Rhincommon tools ?

The Rhino common Module is baulks with some curves. I dont know why. I am hoping to find a module that is more tolerant of whatever the problem is between the two curves posted above.

your input is causing the trouble. The curve that doesn’t fillet has a very tiny radius like transition with radius of approx. 0.002mm…

A good chamfer module would be awesome too.

there were a few solutions to that in this thread

There’s also this.. All it needs is the ability to accept a list of which corners to chamfer, so we can do things like chamfer only concave or convex vertices.

your input is causing the trouble. The curve that doesn’t fillet has a very tiny radius like transition with radius of approx. 0.002mm…

Well yes, the RADIUS at the transition between these two curves is actually ZERO. Rhino created this tiny radius when it joined the curves. But keep in mind the same algorithm created all the other curves, most of which resulted in a FILLET.

Perhaps you could explain why this radius is important and how to join two curves at something approaching a right angle without this resulting micro curve. I am being forced to deal with issues that are not relevant to creating a fillet between two curves much like BLEND does, except a true tangential fillet. I dont need joined curves for that.

But this discussion is irrelevant. The curvature of the join between the two curves is no where near the tangency point resulting from the rolling ball. It just doesnt matter.

The problem is the underling algorithm is too simplistic or requires inputs that cause the problems it then baulks at.

These were originally TWO CURVES that were joined and to be able to feed into the module as ONE CURVE along with a t value of the location of the crease. It should expect this kind of input curve or accept TWO CURVES as an input.

The module that accepts two curves as input doesnt produce a resulting fillet on any of the 60+ curves. No idea why. Thats why I was forced to use this module.

I am again today faced with rewriting a fillet algo from first principals because the resulting surface doesnt follow the curves created, so the brep wont join and volume cant be calculated. Its frustrating.

In this case the fillet gradually fades out alone the length of the curves.

The algo must accommodate a change in the location of the vectors by which the curves are move to find their intersection. If the curves are curved, the error in vectors causes the rolling ball position to be calculated incorrectly.

Ill create a simple demo later and post it

Another problem: It appears that my curves are not TWO single curves in places due to the way they were derived:


So before I do any surfacing, or filleting I must deal with curves that are posing as a single curve from the output of BREP/PLANE INTERSECTION. I assume FIT CURVE will generate a single curve? such that the original curve is matched at the project tolerance setting or CCX will fail further down the page

Then the next problem with the curves is that the direction varies depending on the surface they are derived from. so before creating surfaces, before filleting, before even working on these curves, back to the surfaces that they were derived from and thus back to the curves that made them. Basically scroll back to the top and start again.

The central problem appears to be overlapping at the intersection of some of the curves.


we are talking about .005 or so, but its enough to cause CCX to find 2 intersections between these two curves, and for JOIN to create something that all the FILLET modules baulk at.

Both these curves have been trimmed using the same XY plane, one set of curves above, the other below. They should all meet perfectly at the plane… but no.

1 Like

like this?:

1 Like

It might have to do again with the way your file is set up / tolerances. So yes if you can post example files I’d be interested to take a look at how and why this happens

My tolerance is set to .001. Ill work on an example file. My current idea is to divide one of the curves so 100 points are put on it, then just replace the point where the join is to occur with the first point from the other curve.

WHICH WORKED. all the curves joined nicely. The output of JOIN is completely random (bears no relation to input) so a SORT was required and FILLET produced a fillet on all the curves.

That’s a LOT of blood an sweat to fillet come curves! IT would be nice if it had an output for just the fillet curves

1 Like

Pretty much. Though ideally it’d just accept a list of points so a user could exclude or include any vertex they want to.

One of the fillet components already does that. But as mentioned elsewhere there are other shortcomings with some of the fillet components. Ideally there’d be one fillet, chamfer, blend component for curves, like with solids, and it’d take point input to select what corners get changed, it’d output a single curve, and it’d have an input that was either fillet/blend radius or chamfer length depending on whether the user selected fillet, blend, or chamfer.

Trying to figure this out as a cluster was on my list of things to do but I have so, so many things to do.

for polylines it shouldn’t be too difficult:

This module works well once its input requirements are understood.

It wants two curves that are not connected, so in my case, I divided one of the lines into >100 points and just deleted the point closest to the second line and then rebuilt the curve with INTERPOLATE CURVE.

The advantage of this module is that the fillet curves are available as a separate output. The only way to get these from FILLET is to CCX with the input curves. Unfortunately you run into the limits of tolerance of the CCX module operation with fading fillets rendering the FILLET module useless if you want anything other than joined filleted curves as output.

The secondary benefit to using this module is that the curves do not need to be JOINed - an operation that inexplicably shuffles all the curves in a seemingly random manner requiring a sort operation.

Fillet is not nuclear science … but requires a lot of checks not only related with the result itself (i.e. if a fillet is possible) but with the “reverse” situations as well. Study these indicative captures (with or without the checkReverse option).


@ Peter,
Thank you. Could you please post a .gh file )

Er … I don’t have access to a vast variety of C#'s (in the practice workstations) related with similar stuff.

The above are just some screenshots found in a laptop : I’m in some sort of extended vacation state having sealed the practice for the summer (N95 masks are not my style).

Yes I was sure you already had code for this lying around :slight_smile:
Any hints for reverse checking ?

Are you familiar with C#?