Minimum torsion sweep

Minimum torsion sweep

davemurrayrust
Enthusiast Enthusiast
759 Views
16 Replies
Message 1 of 17

Minimum torsion sweep

davemurrayrust
Enthusiast
Enthusiast

I'm playing with some mathematical knots as a basis for making lighting. I can make a knot, and sweep a profile along it no problem, and it looks great.

 

Eventually, I want to use it as a channel to hold LED strip, which is very flexible in two directions, and not at all in the third, so I'm wondering if there's any way to control the rotation of the profile along the 3d path to minimise the amount of bending needed?

 

The attachment is an example - it's a simple trefoil knot. I could probably *just about* run a strip down the channel here if I keep the tolerances loose, but I would probably get into trouble with more complex shapes.

davemurrayrust_0-1766917187465.png

 

So, is there any generic solution to do a sweep (or similar operation) that has as little 'side to side' torsion as possible?

 

0 Likes
Accepted solutions (1)
760 Views
16 Replies
Replies (16)
Message 2 of 17

johnsonshiue
Community Manager
Community Manager

Hi Dave,

 

I believe you are looking for Guide Surface Sweep. You may pick the XZ plane as the guide surface to minimize the profile twist. Also you want to move the profile plane to the start of the path. Please take a look at the attached f3d file.

 

johnsonshiue_0-1767648344257.png

Many thanks!

 



Johnson Shiue (johnson.shiue@autodesk.com)
Software Test Engineer
0 Likes
Message 3 of 17

TrippyLighting
Consultant
Consultant

@johnsonshiue wrote:

Hi Dave,

 

I believe you are looking for Guide Surface Sweep. ... to minimize the profile twist.

 


While that will create a consistent sweep, that is not going to address the problem. I am not sure sweeping along an arbitrarily curved path through 3D space will address the mechanical problem.

The path curve must have specific non-arbitrary characteristics I have not yet figured out. 


EESignature

0 Likes
Message 4 of 17

davemurrayrust
Enthusiast
Enthusiast

Thanks - I think that doesn't work though, as @TrippyLighting says below. If you look at the loop in the top left of your image, it's bending in a way that a strip could follow, but as that goes round the back, it's bending in a plane at 90 degrees, which is exactly what I want to avoid.

0 Likes
Message 5 of 17

davemurrayrust
Enthusiast
Enthusiast

Agreed. For the overall problem - finding a path that can be followed by e.g. a strip of paper, through a set of points - I think it's possible: just conceptually, you could make this approximate shape with paper/led strip - example attached 😉


 

davemurrayrust_0-1767798588578.png

 

The problem is, I think, there's a relation between the curvature of strip in space and the twist is has to undergo, and without having something that takes that into account, it's going to be hard. It could be that splines are not a good intermediate step, or some conditions have to be made over them? I'm guessing it's beyond what is possible in Fusion without adding in something specifically computational. To illustrate more clearly, here's an open pentafoil - if you look at the curve where path starts on the left, there's no way that a strip could follow that - it would have to rotate counterclockwise around the bend.

 

 

 

davemurrayrust_1-1767798696377.png

If there was some way to angle the sweep to always keep the same orientation relative to the angle of curvature, that would probably do it, or at least get close enough. I guess a rail, if we could construct the right kind of path from the original spline? Offset in the direction of maximal curvature?

 

Message 6 of 17

johnsonshiue
Community Manager
Community Manager

Hi! Here is another attempt using a Ruled Surface as a guide. Though I don't believe it fully reduces the twist, at least section profile remains vertical along the path persistently.

To get the desirable result, you may need to create multiple section profiles and use Loft to go through them. To ensure consistent thickness, you could simply create a Loft surface based a series of line profiles. Next, thicken the surface into a solid body. Shell the solid body to create the groove.

 

johnsonshiue_0-1767808443003.png

johnsonshiue_1-1767808520619.png

Many thanks!



Johnson Shiue (johnson.shiue@autodesk.com)
Software Test Engineer
0 Likes
Message 7 of 17

MichaelT_123
Advisor
Advisor

Hi Mr DaveMrrayrust,

 

Here is another 'macka geegee' attempt. If I understood your design intention correctly, I created the trefoil body by stitching its loft faces, making sure that they obeyed the 'horizontal/vertical paradigm as required. I don't think a detailed explanation of 'macka geegee' steps is necessary ... as they are all so natural.

Natural ... in the case of Z-axis with 'light' projections up-green and down-red.

However, the illumination vector can point in any arbitrary direction. In such a situation, the "horizontal" faces should be "lofted" from line segments perpendicular (the closed distance points) to the chosen axis.

Trefoil_Z.png

 

 

 

Regards

MichaelT

 

PS.

FYI: https://forums.autodesk.com/t5/fusion-design-validate-document/braids-in-our-lives/m-p/13300468

 

MichaelT
0 Likes
Message 8 of 17

davemurrayrust
Enthusiast
Enthusiast

Thanks @MichaelT_123 - actually, though this is going in the opposite direction to what I need: I'm not trying to keep the faces horizontal vertical, although that's what @johnsonshiue 's last example did. I'm trying to create a shape that an LED strip (or paper ribbon) could be placed into, with only the kinds of twisting that it's happy with - essentially a twist along the long axis, and curvature in the narrow axis but not the broad one. Hopefully this makes things clearer:

Screenshot 2026-01-08 at 17.14.13.png

 

The braids you put in are gorgeous, though - will have to dig in a bit.

 

My current thinking is that either i) I need to remember some maths, dig out PyKnotID to construct some curves, and then do something fancy to construct rails to run a sweep along than minimises Z twist or ii) I put a bunch of copies of the sketch profile at important corners, tweak their rotation by hand and then loft using the base spline as a guide. Neither fill me with joy!

0 Likes
Message 9 of 17

davemurrayrust
Enthusiast
Enthusiast

I've been building a bit on the ideas from @MichaelT_123 and @johnsonshiue , and the idea that I can't set everything automatically, but if I can manually adjust the rotation of the sweep at certain points, I can tweak it by hand to get something workable. My current approach is looking a bit like this (file attached):

davemurrayrust_0-1769962337196.png

So, I:

- make a 3d spline

- create perpendicular planes along this spline, and put a 10mm line centred on the spline that can be angled

- draw 2 more splines through the end points of these lines (so it keeps the 10mm distance, roughly, all the way along)

- now I can sweep my profile along one of the splines, with the other as a guide rail.

 

It works, but

- it's really laborious. I could maybe write a script

- it's a real hack - because it's impossible to sensibly make a plane perpendicular at a point along a 3d spline, I can't easily replicate the original spline

- if I need to add more control points, I have to recreate the secondary splines, and that's a pain.

 

So, I'm wondering if anyone has a nicer way to do this kind of thing? Perhaps something along the lines of putting the profile in a component, doing a "Duplicate with joints" along the spline, and then rotating each one - but this seems to still need a lot of planes+sketches made along the path to anchor the joints to...

 

One caveat is I don't want to use the thickening etc. operations above - I need control of the swept profile as it needs to fit other components.

 

Any ideas welcome! Thanks!

Message 10 of 17

johnsonshiue
Community Manager
Community Manager

Hi Dave,

 

Many thanks for sharing the findings! I suspect you need a custom Sweep, the profile orienting to a predefined vector field along the Sweep Path. At the moment, I don't believe the Sweep command itself allows that. The closest thing I can think of right now is to use a Guide Rail which can help orienting the profile (pretty much the same process as you have developed). This Guide Rail has to keep the same offset distance with the Sweep Path, so that the profile scale remains the same.

Thanks again!

 



Johnson Shiue (johnson.shiue@autodesk.com)
Software Test Engineer
0 Likes
Message 11 of 17

davemurrayrust
Enthusiast
Enthusiast

Thanks @johnsonshiue, great to have context.

 

I've found a slightly simpler way to do a sweep where I control the angle along the path: do the sweep in sections, use the end of each section as the start of the next section, and set the rotation amount per section to get desired angles. It's definitely not perfect! In particular, rotating one way and then the other makes a sharp change, rather than the smoothness of splines. It's also a bit fiddly getting the numbers right in Fusion, partly because I'm not sure what the two distances along the path are supposed to be. But - it's much easier than making lots of planes, and doesn't involve an intermediate spline that's ignored in the end.

 

davemurrayrust_0-1770289776130.png

It would be lovely to have a sweep command where one could set the angle at particular points along the path!

Message 12 of 17

johnsonshiue
Community Manager
Community Manager

Hi Dave,

 

Before the Twist Vector Sweep is fully implemented, there is a way to "sort of" control the profile orientation by using a Loft surface. Simply create a series of Work Planes along the path and create lines of equal length, serving as handles. Create a Loft surface passing through these lines along the path. Lastly, use Guide Surface Sweep (based on the Loft surface) or Guide Rail Sweep (based on the Loft surface edge) to create the body.

You may rotate the handles to twist the Sweep. However, it is predicated by the Loft. If the Loft fails, the Sweep will fail accordingly.

 

johnsonshiue_0-1770315587112.png

Many thanks!



Johnson Shiue (johnson.shiue@autodesk.com)
Software Test Engineer
0 Likes
Message 13 of 17

davemurrayrust
Enthusiast
Enthusiast

Thanks @johnsonshiue  this seems to be the best approach at the moment. Just to check, are Work Planes something special, or does Plane Tangent To Path capture what you mean?

 

I've made a quick script to create the planes at regular intervals and add a line to each one.

Screenshot 2026-02-06 at 10.57.30.png

 

Then I can sweep along that surface and get some suitably gnarly shapes:

Screenshot 2026-02-06 at 11.01.30.png

 

So now I just need to add proper constraints to the sketch geometry, and it starts looking pretty good, especially as the points can be edited without going into the sketches so it's all fairly fluid.

 

 

Message 14 of 17

davemurrayrust
Enthusiast
Enthusiast

OK, here's a script that constructs the planes and the Surface Loft in one go. I can't get it to do the final Sweep yet - fighting with the API (https://forums.autodesk.com/t5/fusion-design-validate-document/minimal-working-example-of-creating-a...) - but it's fairly usable as is.

 

davemurrayrust_0-1770383406775.png

One thing I can't figure out is that Fusion seems to concentrate the wiggliness of the Sweep differently to the guide surface. For instance given this curve:

davemurrayrust_1-1770383705142.png

 

The body created has all the torsion concentrated in the middle:

davemurrayrust_2-1770383733608.png

 

So it doesn't really seem to be following the surface properly. But at least for gentle curves, it works relatively sensibly.

 

Next step, I might look into constructing the lines at the 'correct' angle so that they are in the plane of the splines instantaneous curvature. 

 

I can't attach a .py file, so here's the full script

 

# Fusion 360 Script: Planes + Sketches + Perpendicular Lines along a 3D spline
#
# Basic Script Command pattern (see Fusion Commands doc):
# https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-3922697A-7BF1-4799-9A5B-C8539DF57051
#
# How to use:
# - UTILITIES > Add-Ins > Scripts and Add-Ins... > Scripts tab > + (add this .py) > Run
# - A single command dialog appears: select spline, set options, click OK.

import adsk.core
import adsk.fusion
import traceback

_app = None
_ui = None
# Keep event handlers in scope (required for Python scripts using commands)
handlers = []
CMD_ID = "SplinePlanesLoftCmd"


def _do_create_planes_and_lines(design, sketch_curve, plane_count, line_len_cm):
    """Creates planes, sketches, and lines along the spline. Returns (created_count, list_of_lines)."""
    root_comp = design.rootComponent
    path = root_comp.features.createPath(sketch_curve, False)

    if plane_count == 1:
        proportions = [0.5]
    else:
        # Always include endpoints: 0, 1/(n-1), ..., 1
        proportions = [i / (plane_count - 1) for i in range(plane_count)]

    planes = root_comp.constructionPlanes
    sketches = root_comp.sketches
    created_lines = []
    created = 0

    for t in proportions:
        plane_input = planes.createInput()
        plane_input.setByDistanceOnPath(path, adsk.core.ValueInput.createByReal(t))
        cplane = planes.add(plane_input)
        sk = sketches.add(cplane)

        start_pt = adsk.core.Point3D.create(0, 0, 0)
        end_pt = adsk.core.Point3D.create(line_len_cm, 0, 0)
        line = sk.sketchCurves.sketchLines.addByTwoPoints(start_pt, end_pt)
        line.startSketchPoint.isFixed = True

        text_pt = adsk.core.Point3D.create(line_len_cm / 2, 0, 0)
        length_dim = sk.sketchDimensions.addDistanceDimension(
            line.startSketchPoint,
            line.endSketchPoint,
            adsk.fusion.DimensionOrientations.AlignedDimensionOrientation,
            text_pt,
            True
        )
        length_dim.value = line_len_cm

        created_lines.append(line)
        created += 1

    return created, created_lines


def _create_loft_surface(root_comp, sketch_curve, line_list):
    """
    Creates a Surface Loft (isSolid=False) using the given lines as sections
    and sketch_curve as guide rail.
    """
    loft_feats = root_comp.features.loftFeatures
    loft_input = loft_feats.createInput(
        adsk.fusion.FeatureOperations.NewBodyFeatureOperation
    )
    # Surface loft: isSolid=False creates a surface body, not a solid
    loft_input.isSolid = False
    loft_input.isClosed = False
    loft_input.startLoftEdgeAlignment = (
        adsk.fusion.LoftEdgeAlignments.FreeEdgesLoftEdgeAlignment
    )
    loft_input.endLoftEdgeAlignment = (
        adsk.fusion.LoftEdgeAlignments.FreeEdgesLoftEdgeAlignment
    )

    sections = loft_input.loftSections
    for line in line_list:
        sections.add(line)

    # Add spline as guide rail (per Fusion API: LoftCenterLineOrRails.addRail)
    if hasattr(loft_input, "centerLineOrRails"):
        loft_input.centerLineOrRails.addRail(sketch_curve)

    return loft_feats.add(loft_input)


# --- Command event handlers (Basic Script Command pattern) ---


class SplinePlanesCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        event_args = adsk.core.CommandCreatedEventArgs.cast(args)
        cmd = event_args.command
        inputs = cmd.commandInputs

        app = adsk.core.Application.get()
        design = adsk.fusion.Design.cast(app.activeProduct)
        if not design:
            return

        inputs.addSelectionInput(
            "spline",
            "Spline",
            "Select a 3D spline (sketch curve)"
        )
        sel_in = inputs.itemById("spline")
        sel_in.addSelectionFilter("SketchCurves")
        sel_in.setSelectionLimits(1, 1)

        inputs.addDistanceValueCommandInput(
            "normal_line_len",
            "Line length",
            adsk.core.ValueInput.createByReal(1)
        )
        inputs.addIntegerSpinnerCommandInput(
            "planes",
            "Number of planes",
            1, 200, 1, 10
        )

        on_execute = SplinePlanesCommandExecuteHandler()
        cmd.execute.add(on_execute)
        handlers.append(on_execute)


class SplinePlanesCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        event_args = adsk.core.CommandEventArgs.cast(args)
        app = adsk.core.Application.get()
        ui = app.userInterface

        try:
            design = adsk.fusion.Design.cast(app.activeProduct)
            if not design:
                ui.messageBox("No active design.")
                adsk.terminate()
                return

            root_comp = design.rootComponent
            cmd_inputs = event_args.command.commandInputs

            sel_in = cmd_inputs.itemById("spline")
            if sel_in.selectionCount < 1:
                ui.messageBox("Please select a spline.")
                adsk.terminate()
                return

            entity = sel_in.selection(0).entity
            sketch_curve = adsk.fusion.SketchCurve.cast(entity)
            if not sketch_curve:
                ui.messageBox("Selection must be a sketch curve (spline).")
                adsk.terminate()
                return

            plane_count = int(cmd_inputs.itemById("planes").value)
            # DistanceValueCommandInput.value is in internal units (cm)
            line_len_cm = cmd_inputs.itemById("normal_line_len").value

            created, line_list = _do_create_planes_and_lines(
                design, sketch_curve, plane_count, line_len_cm
            )
            _create_loft_surface(root_comp, sketch_curve, line_list)

            ui.messageBox(
                "Done.\nCreated {} planes + sketches + lines and surface loft.".format(created)
            )
        except Exception:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))
        finally:
            adsk.terminate()


def run(context):
    global _app, _ui
    _app = adsk.core.Application.get()
    _ui = _app.userInterface

    try:
        design = adsk.fusion.Design.cast(_app.activeProduct)
        if not design:
            _ui.messageBox("No active Fusion design. Please open a design and try again.")
            return

        cmd_defs = _ui.commandDefinitions
        cmd_def = cmd_defs.itemById(CMD_ID)
        if cmd_def:
            cmd_def.deleteMe()

        cmd_def = cmd_defs.addButtonDefinition(
            CMD_ID,
            "Planes + Loft along spline",
            "Create planes, perpendicular lines, and surface loft along a 3D spline"
        )

        created_handler = SplinePlanesCommandCreatedHandler()
        cmd_def.commandCreated.add(created_handler)
        handlers.append(created_handler)

        cmd_def.execute()
        adsk.autoTerminate(False)
    except Exception:
        if _ui:
            _ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def stop(context):
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        cmd_def = ui.commandDefinitions.itemById(CMD_ID)
        if cmd_def:
            cmd_def.deleteMe()
    except Exception:
        pass

 

Message 15 of 17

johnsonshiue
Community Manager
Community Manager

Hi Dave,

 

I am sorry that I used the wrong term in Fusion. I was talking about "Plane Along Path." The command ensures the plane is normal to the path at any given point.

Many thanks!



Johnson Shiue (johnson.shiue@autodesk.com)
Software Test Engineer
0 Likes
Message 16 of 17

davemurrayrust
Enthusiast
Enthusiast

No worries - I got what you meant, I was just checking it wasn't some special magic I didn't know about yet. And you put me back on the path to a decent solution

0 Likes
Message 17 of 17

davemurrayrust
Enthusiast
Enthusiast
Accepted solution

Just to close this topic off, here's what it looks like in use - a 3D strip with a bunch of control handles:

davemurrayrust_0-1770546743389.png

 

 

I can then sweep along that:

davemurrayrust_1-1770546784468.png

 

And get a fairly sensible solid object out:

davemurrayrust_3-1770546831167.png

davemurrayrust_4-1770546976355.png

 

 

It's still a pain editing the curve - each time I move a handle it's 20s of processing, and they're quite fiddly! - but it works. Thanks @johnsonshiue @MichaelT_123 for all your suggestions.