Fusion360 + 3DPrinting algorithm help

Fusion360 + 3DPrinting algorithm help

and.magro
Advocate Advocate
1,877 Views
10 Replies
Message 1 of 11

Fusion360 + 3DPrinting algorithm help

and.magro
Advocate
Advocate

Hello,

 

I’ve been doing some educational projects with Fusion360 and 3D Printing.
I designed some simple structures - bridges and lampshades, and modeled the connectors and then with a BEETHEFRIST 3D printer the connectors were printed, and finally the structures were assembled.

 

2016-08-29 11.12.58.jpg2016-08-29 11.12.22.jpg


Now I intend to work with more complex and large scale structures, but it will be impossible to model each connector manually, so I came for your help.

 

structu.jpg

 

I already started looking for Fusion360 API in order to create an algorithm to automatically generated all the connectors from the desired structure.

The dimensions to control in the connectors are: length, interior diameter and exterior diameter.


I think the steps to create the algorithm are:

1 - find sketch lines
2 - identify start and end of the lines
3 - for each start and end of lines create a “pipe”
4 - combine the “pipes”

 

I know that it already exists a “pipe” script in the “Scripts an Add-ons” in Fusion360 with good documentation, but I don’t have much knowledge in Python and this seems to be a really difficulty task.

Once again I really appreciate if anyone could help me with this.


Thanks

0 Likes
1,878 Views
10 Replies
Replies (10)
Message 2 of 11

ekinsb
Alumni
Alumni

I have some questions about the sketch lines that would affect my approach to the problem.  

 

  • Are all of the lines in a single sketch?  
  • Do the lines that come together at an intersection all share the same point?  That is, if you move one of the points, do all of the lines move with it?  Or are they completely individual and their start and end points just happen to be at the same location?

If the lines do share a common point then my approach would be to get all of the points and then use that connectivity information to find all of the lines that come together at that intersection and then construct the connector.  If the lines do not share a common point then I would still want to take a similar approach but I would need to do an extra step of calculating which lines connect to which corners.  I would also do the modeling in either a direct edit model or in a base feature.  In both cases no parametric data is captured and the construction will be much faster. 


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
Message 3 of 11

and.magro
Advocate
Advocate

Hello ekinsb,

 

 

  • Are all of the lines in a single sketch? No

In this first phase I will generate the structures in Fusion, and I will design in different sketches.

Later on, the structures will be generated outside Fusion - I will use nTopology Element (a lattice topology generator) - when imported to Fusion I think the entire structure will be on a single sketch. But for now let's assume that the lines will be designed in different sketches.

 

  • Do the lines that come together at an intersection all share the same point? No 
    • That is, if you move one of the points, do all of the lines move with it? No
    • Or are they completely individual and their start and end points just happen to be at the same location? Yes

 

Thanks for your help.

0 Likes
Message 4 of 11

ekinsb
Alumni
Alumni

 

I was going to write up the general approach I would take but once this is an interesting problem and I couldn't help but write some code.  The coding went smoothly so I went ahead and finished it.  Here's the results of what the program below creates.  The code currently looks for all sketches in the root component that have "Connect" as part of the name.  For example I had a sketch named "Sketch 1 (Connect)" and "Sketch 2 (Connect)".  It passes this list of sketches to a function that then builds all of the connectors.  The different inputs are hard coded in the program (length, outer diameter, hole diameter, and the maximum distance two points can be away from each other to be considered the same.  And since you're 3D printing them I thought why not add fillets between the cylinders in the connector, so there's also a variable for the fillet radius.  Each connector is a separate component.

 

connectors.png

 

There are probably some simpler ways to approach this but this is my quick solution.  Hopefully you'll be able to figure out what I'm doing but I know some of it's not obvious and I got lazy with my commenting because I was in a hurry and just wanted to see if I could get it to work but I don't have any more time I can spend on it.

 

import adsk.core, adsk.fusion, adsk.cam, traceback
import math

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)

connectorDia = 1
legLength = 2
holeDia = 0.6
holeDepth = 1
filletRad = 0.1
tolerance = 0.05

class ConnectionPoint(object):
    pointIndexCount = 0
    
    def __init__(self, coordinate):
        self.coordinate = coordinate
        self.pointIndex = self.pointIndexCount
        ConnectionPoint.pointIndexCount += 1
        self.connectedPoints = []       
            
    def addConnection(self, connectedPointIndex):
        self.connectedPoints.append(connectedPointIndex)


def calculateConnections(sketches, tolerance):
    try:
        connections = []
            
        sketch = adsk.fusion.Sketch.cast(None)
        for sketch in sketches:
            # Iterate through the lines in each sketch.
            line = adsk.fusion.SketchLine.cast(None)
            for line in sketch.sketchCurves.sketchLines:
                startPnt = line.startSketchPoint.geometry
                endPnt = line.endSketchPoint.geometry
                
                # Check to see if the start point is already in the point list.
                foundPoint = False
                for currentConnection in connections:
                    if startPnt.distanceTo(currentConnection.coordinate) <= tolerance:
                        foundPoint = True
                        
                        # Check if the end point is in the list.
                        foundOtherPoint = False
                        for otherConnection in connections:
                            if endPnt.distanceTo(otherConnection.coordinate) <= tolerance:
                                foundOtherPoint = True
                                
                                currentConnection.addConnection(otherConnection.pointIndex)                               
                        if not foundOtherPoint:
                            # Create a new connection point.
                            connectPoint = ConnectionPoint(endPnt)
                            connections.append(connectPoint)
                            currentConnection.addConnection(connectPoint.pointIndex)                           
                    elif endPnt.distanceTo(currentConnection.coordinate) <= tolerance:
                        foundPoint = True
                        
                        # Check if the end point is in the list.
                        foundOtherPoint = False
                        for otherConnection in connections:
                            if startPnt.distanceTo(otherConnection.coordinate) <= tolerance:
                                foundOtherPoint = True
                                
                                currentConnection.addConnection(otherConnection.pointIndex)                                
                        if not foundOtherPoint:
                            # Create a new connection point.
                            connectPoint = ConnectionPoint(startPnt)
                            connections.append(connectPoint)
                            currentConnection.addConnection(connectPoint.pointIndex)
                            
                if not foundPoint:
                    # The points weren't found at all so add both of them as new connections.
                    connectOne = ConnectionPoint(startPnt)
                    connections.append(connectOne)
    
                    connectTwo = ConnectionPoint(endPnt)
                    connectTwo.addConnection(connectOne.pointIndex)                    
                    connections.append(connectTwo)
                    connectOne.addConnection(connectTwo.pointIndex)
                    
        return connections
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def drawConnector(root2, connections, index, name):
    try:
        root = adsk.fusion.Component.cast(root2)
        occ = root.occurrences.addNewComponent(adsk.core.Matrix3D.create())
        comp = occ.component
        comp.name = name
    
        planeInput = comp.constructionPlanes.createInput()
        originPnt = adsk.core.Point3D.cast(connections[index].coordinate)
    
        first = True    
        for connectedIndex in connections[index].connectedPoints:            
            directionPnt = connections[connectedIndex].coordinate
            planeInput.setByPlane(adsk.core.Plane.create(originPnt, originPnt.vectorTo(directionPnt)))
            constPlane = comp.constructionPlanes.add(planeInput)
            
            sk = comp.sketches.add(constPlane)
            sk.sketchCurves.sketchCircles.addByCenterRadius(adsk.core.Point3D.create(0,0,0), connectorDia/2.0)
    
            if first:
                extInput = comp.features.extrudeFeatures.createInput(sk.profiles.item(0), adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
                first = False
            else:
                extInput = comp.features.extrudeFeatures.createInput(sk.profiles.item(0), adsk.fusion.FeatureOperations.JoinFeatureOperation)
    
            extInput.setDistanceExtent(False, adsk.core.ValueInput.createByReal(legLength))       
            comp.features.extrudeFeatures.add(extInput)
    
            holeOrigin = originPnt.copy()
            offsetDir = holeOrigin.vectorTo(directionPnt)
            offsetDir.normalize()
            offsetDir.scaleBy(legLength - holeDepth)
            holeOrigin.translateBy(offsetDir)
            planeInput.setByPlane(adsk.core.Plane.create(holeOrigin, offsetDir))
            holeConstPlane = comp.constructionPlanes.add(planeInput)
            
            sk = comp.sketches.add(holeConstPlane)
            sk.sketchCurves.sketchCircles.addByCenterRadius(adsk.core.Point3D.create(0,0,0), holeDia/2.0)
            extInput = comp.features.extrudeFeatures.createInput(sk.profiles.item(0), adsk.fusion.FeatureOperations.CutFeatureOperation)
    
            extInput.setDistanceExtent(False, adsk.core.ValueInput.createByReal(holeDepth))       
            extrude = comp.features.extrudeFeatures.add(extInput)
            
        sk = comp.sketches.add(constPlane)
        midLine = sk.sketchCurves.sketchLines.addByTwoPoints(adsk.core.Point3D.create(0, connectorDia/2, 0), adsk.core.Point3D.create(0, -connectorDia/2, 0))
        arc = sk.sketchCurves.sketchArcs.addByThreePoints(midLine.startSketchPoint, adsk.core.Point3D.create(connectorDia/2, 0, 0), midLine.endSketchPoint)
        
        revInput = comp.features.revolveFeatures.createInput(sk.profiles.item(0), midLine, adsk.fusion.FeatureOperations.JoinFeatureOperation)
        revInput.setAngleExtent(False, adsk.core.ValueInput.createByReal(math.pi*2))
        try:
            rev = comp.features.revolveFeatures.add(revInput)
        except:
            pass

        body = extrude.bodies.item(0)
    
        filletInput = adsk.fusion.FilletFeatureInput.cast(comp.features.filletFeatures.createInput())
        filletEdges = adsk.core.ObjectCollection.create()
        
        edge = adsk.fusion.BRepEdge.cast(None)
        for edge in body.edges:
            if (edge.faces.item(0).geometry.objectType == adsk.core.Cylinder.classType() and
                edge.faces.item(1).geometry.objectType == adsk.core.Cylinder.classType()):
                filletEdges.add(edge)  
    
        filletInput.addConstantRadiusEdgeSet(filletEdges, adsk.core.ValueInput.createByReal(filletRad), False)
        comp.features.filletFeatures.add(filletInput)
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

        
def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui  = _app.userInterface
        
        des = adsk.fusion.Design.cast(_app.activeProduct)
        if des.designType != adsk.fusion.DesignTypes.DirectDesignType:
            _ui.messageBox('The design must be a direct edit design. use the context menu of the top node in the browser and choose "Do not capture Design History".')
            return False

        root = des.rootComponent
                 
        sketches = []
        for sk in root.sketches:
            if '(Connect)' in sk.name:
                sketches.append(sk)

        if len(sketches) == 0:
            _ui.messageBox('No sketches with "(Connect)" were found.')
            return False
            
        connections = calculateConnections(sketches, tolerance)
        
        for i in range(0, len(connections)):        
            name = "Connector " + str(i)
            drawConnector(root, connections, i, name) 
            
        des.activateRootComponent()
                                    
    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
Message 5 of 11

and.magro
Advocate
Advocate

Hello ekinsb,

 

Thank you very very much for your time.
I'm having some issues with the intersections in sketches, in some intersections the connectors are not being generated.
Could you please send me your Fusion project?


Thanks

0 Likes
Message 6 of 11

ekinsb
Alumni
Alumni

Attached is the Fusion file I used for testing and also the complete script.  I made a small change to it so that it reports when the fillet creation fails.

 

I'm wondering if you have cases where the lines don't come together (within a tolerance) at their ends.  For example if you have a line and then have another line that connects to the midpoint of that line to create a T.  Because the connection is not at the line end points it is not recognized as a connection.  If the first line is split where the other line connects, then it will work.

 

If that's not the issue and there's something else I can see if it's a simple issue to address or not.


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
Message 7 of 11

and.magro
Advocate
Advocate

Hello,

 

I was doing simple tests, in the pictures bellow I will show what is happening and the issues that I've found.

 

For example, if I design a sketch with lines only in the same plane, some issues will occur during the connectors generation.

 

Captura de ecrã 2016-10-20, às 20.38.20.png

 

Captura de ecrã 2016-10-20, às 20.38.47.png

 

Captura de ecrã 2016-10-20, às 20.39.07.png

 

But then if I move vertically some intersection points of the same sketch, I don't find the previous issue. 

Captura de ecrã 2016-10-20, às 20.36.50.png


Captura de ecrã 2016-10-20, às 20.37.06.png

 

Here's another issue:

Captura de ecrã 2016-10-20, às 21.33.15.png

 

Attached there are the files with Fusion projects.

 

Thanks.

0 Likes
Message 8 of 11

ekinsb
Alumni
Alumni

The problem with the house model is that I was getting the points in sketch space instead of model space.  That was an easy fix.

 

The problem with the rectangle is what I think is an arguable bug in the API but I need to discuss it with some others before I log a bug.  The problem is that if the construction plane I'm creating a sketch is parallel to the base construction planes it creates the sketch so the normal of the sketch is the same as the base construction planes instead of the construction plane I'm creating it on.  That causes some of the extrusions to go in the wrong direction.  If you leave everything flat and rotate the sketch a fraction of a degree, then it all works because the new construction planes aren't parallel to the base construction planes.

 

I've added a workaround in the code to sketch if the sketch normal is in the expected direction or not and reverse the extrude direction if it's not.  The new script is attached.


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
Message 9 of 11

and.magro
Advocate
Advocate

Hello,

 

Thanks for the script update, the generation of the connectors is now much faster.

 

As you suggest I rotated the rectangle some degrees, and it solved the problem, by doing this the fillets of the outside connectors are not generated, but for me it's ok.

 

Captura de ecrã 2016-10-20, às 23.40.52.png

 

Captura de ecrã 2016-10-20, às 23.40.12.png

 

I will be doing some more tests, if I detect other problems I will let you know.

By the way, what is the "LineConnectors.manifest" file?

 

Again thank you very much for all your support!

 

 

André

 

0 Likes
Message 10 of 11

ekinsb
Alumni
Alumni

The .manifest file just holds some other data associated with the script. It's not absolutely required for a script but is needed for an add-in.  When you create a new script using the Scripts and Add-Ins dialog it will create a new folder and create a .py and .manifest file in that folder where the folder and the files all have the same name.


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
0 Likes
Message 11 of 11

ekinsb
Alumni
Alumni

Also, the fillets probably failed because they were too big to fit in between the cylinders.  If you reduce the size of the fillet radius it should work.


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
0 Likes