Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

"Shaper" plugin disappeared??

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
plowry01
1934 Views, 11 Replies

"Shaper" plugin disappeared??

My work heavily relied on saving faces as an .svg which the shaper plugin did just fine, but to my horror today I find that its gone! when I open Fusion yesterday it was under the "tools" menu but today its gone. Attempts to reinstall fail, I cannot get it back. What happened to it!!

11 REPLIES 11
Message 2 of 12
HughesTooling
in reply to: plowry01

If you press Shift+S does it show in the add ins dialog? If it's an add in is it running and set to auto run?

image.png

Mark

Mark Hughes
Owner, Hughes Tooling
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Message 3 of 12
plowry01
in reply to: HughesTooling

It is listed but not active? clicking run doesn't do anything. I'm curious if its just my problem or a fusion/shaper problem as it just happened out of the blue. 

Message 4 of 12
wmhazzard
in reply to: plowry01

The same thing happened to my Text on Arc plugin and the reason was that Fusion went to new Python version 3.7 and the plugin was 3.5 so it would not work any more. The creator of the program had to update the program and when I re installed it, it worked. 

Message 5 of 12
JeromeBriot
in reply to: plowry01

Hello,

 

You have to edit the file ShaperUtilities.py and move all the global variables definitions at the top the function in which there are declared.

 

Here is the modified script:

#Author- Brian Ekins
#Description- Various utilities to make it easy to use Fusion 360 designs with the Shaper Origin router.

import adsk.core, adsk.fusion, traceback, math, tempfile, os
from .MyGeomMath import getSVG, getLayoutSVG, MyBoard, createCompositesFromSketch, createCompositesFromFaces, createCompositesFromProfiles, setLogFile

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
_appPath = os.path.dirname(os.path.abspath(__file__))
_handlers = []
_version = '1.4.1'
_firstActivate = False
_logFile = None
_isBeta = False

# Command inputs.
_topLogo = adsk.core.ImageCommandInput.cast(None)

_typeList = adsk.core.DropDownCommandInput.cast(None)
_selectInput = adsk.core.SelectionCommandInput.cast(None)
_textNotes = adsk.core.TextBoxCommandInput.cast(None)
_advanced = adsk.core.BoolValueCommandInput.cast(None)
_shaperLink = adsk.core.TextBoxCommandInput.cast(None)
_versionText = adsk.core.TextBoxCommandInput.cast(None)
#_showOvercuts = adsk.core.BoolValueCommandInput.cast(None)

#_solidBodyPrompt = 'Select a "top" face on the body to export.'
_solidBodyPrompt = 'Select a single face to define the top cutting surface of a shape, all details cuttable from the top will be exported as an appropriately color coded SVG file.'
_layoutsPrompt = 'Select bodies lying on the X-Y plane.'
_profilePrompt = 'Select the profiles within a single sketch to be exported. Smart color coding is not active, each profilie is treated as a <i>cut outside</i> shape. Plan on changing the cut types on the tool.'
_sketchPrompt = 'Select a sketch to export. The entire contents of the sketch will be exported.'
_facePrompt = 'Selected parallel faces from the same body will be exported. Smart color coding is not active and each face is treated as a <i>cut outside</i> shape. Plan on changing the cut types on the tool.'

_shaperLinkText = '<div align="center"><a href="http:shapertools.com">www.shapertools.com</a></div>'


# Event handler for the commandCreated event.
class ExportCommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        global _isBeta
        global _topLogo
        global _typeList
        global _showOvercuts
        global _selectInput
        global _textNotes
        global _advanced
        global _shaperLink
        global _versionText
        global _firstActivate
        try:
            eventArgs = adsk.core.CommandCreatedEventArgs.cast(args)
            cmd = eventArgs.command
            
            cmd.helpFile = 'Resources/Help/ExportToOriginHelp.html'
            inputs = cmd.commandInputs

            # Set the default values.
            advancedState = False
            isAdvanced = False
            typeState = 'Single Solid Body'
            
            # Read the current defaults:
            if os.path.isfile(os.path.join(_appPath, 'defaults.dat')):
                file = open(os.path.join(_appPath, 'defaults.dat'))
                data = file.readlines()[0]
                file.close()

                settings = data.split(';')
                for setting in settings:
                    settingData = setting.split(':')
                    
                    if settingData[0] == 'advanced':
                        if settingData[1] == 'True':
                            isAdvanced = True
                        else:
                            isAdvanced = False
                    elif settingData[0] == 'type':
                        typeState = settingData[1]
                    elif settingData[0] == 'beta':
                        if settingData[1] == 'True':
                            _isBeta = True
                        else:
                            _isBeta = False
#                advancedState = data.split(';')[0].split(':')[1]
#                if advancedState == 'True':
#                    isAdvanced = True
#                else:
#                    isAdvanced = False
#                typeState = data.split(';')[1].split(':')[1]
                
            # Set the size of the dialog.
            cmd.setDialogInitialSize(425, 275)
            cmd.setDialogMinimumSize(425, 275)
            
            _topLogo = inputs.addImageCommandInput('image', '', 'Resources/shaperExport/ShaperOriginBlack_sm.png')
            _topLogo.isVisible = True
            
            # Get the type of input.
            _typeList = inputs.addDropDownCommandInput('typeList', 'Input type', adsk.core.DropDownStyles.LabeledIconDropDownStyle)
            
            if typeState == 'Single Solid Body':
                _typeList.listItems.add('Single Solid Body', True, 'Resources/SingleBody')
            else:
                _typeList.listItems.add('Single Solid Body', False, 'Resources/SingleBody')

            if typeState == 'Solid Bodies Layout':                
                _typeList.listItems.add('Solid Bodies Layout', True, 'Resources/Layouts')
            else:
                _typeList.listItems.add('Solid Bodies Layout', False, 'Resources/Layouts')

            if typeState == 'Faces':
                _typeList.listItems.add('Faces', True, 'Resources/Faces')
            else:
                _typeList.listItems.add('Faces', False, 'Resources/Faces')

            if typeState == 'Entire Sketch':
                _typeList.listItems.add('Entire Sketch', True, 'Resources/Sketch')
            else:
                _typeList.listItems.add('Entire Sketch', False, 'Resources/Sketch')

            if typeState == 'Sketch Profiles':
                _typeList.listItems.add('Sketch Profiles', True, 'Resources/Profiles')
            else:
                _typeList.listItems.add('Sketch Profiles', False, 'Resources/Profiles')

            if isAdvanced:
                _typeList.isVisible = True
            else:
                _typeList.isVisible = False

            _showOvercuts = inputs.addBoolValueInput('showOvercuts', 'Show Overcuts', True, '', False)
            _showOvercuts.isVisible = False
    
            _selectInput = inputs.addSelectionInput('selection', 'Selection', 'Select the entity to export.')

            _textNotes = inputs.addTextBoxCommandInput('notes', '', '', 2, True)
            _textNotes.isFullWidth = False
            _textNotes.isVisible = True
            
            if isAdvanced:
                _advanced = inputs.addBoolValueInput('Advanced', 'Advanced', True, '', True)
            else:
                _advanced = inputs.addBoolValueInput('Advanced', 'Advanced', True, '', False)
                
            _advanced.isVisible = True

            _shaperLink = inputs.addTextBoxCommandInput('fullWidth_textBox', '',  _shaperLinkText, 1, True)
            _shaperLink.isFullWidth = True
            _shaperLink.isReadOnly = True
            
            versionText = '<div align="right">Version ' + _version + '</div>'
            _versionText = inputs.addTextBoxCommandInput('version_textBox', '', versionText, 1, True)
    
            # Connect to the command related events.
            onExecute = ExportCommandExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)
            
            onInputChanged = ExportCommandInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)
            
            onSelectionEvent = ExportCommandSelectionEventHandler()
            cmd.selectionEvent.add(onSelectionEvent)
            _handlers.append(onSelectionEvent)
            
            onActivate = ExportCommandActivateHandler()
            cmd.activate.add(onActivate)
            _handlers.append(onActivate)
            
            _firstActivate = True
        except:
            if _logFile:
                _logFile.write('Failed in ExportCommandCreatedEventHandler')
                _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
                _logFile.flush()
                
            if _ui:
                _ui.messageBox('An unexpected error occurred in the Shaper Origin Export command.' +
                               '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                               adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


# Event handler for the selectionEvent event.
class ExportCommandSelectionEventHandler(adsk.core.SelectionEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.SelectionEventArgs.cast(args)

            cmd = eventArgs.firingEvent.sender
            typeList = adsk.core.DropDownCommandInput.cast(cmd.commandInputs.itemById('typeList'))
            selInput = eventArgs.activeInput
            if typeList.selectedItem.name == 'Single Solid Body':
                # Get the selected face.
                fc = adsk.fusion.BRepFace.cast(eventArgs.selection.entity)

                # Get the parent body.
                body = fc.body

                # Make sure the body is solid.
                if not body.isSolid:
                    eventArgs.isSelectable = False
                    return

                # Highlight the parent body using custom graphics.
#                if fc:
#                    # Draw the body using custom graphics to show it as being highlighted.
#                    des = adsk.fusion.Design.cast(_app.activeProduct)
#                    root = des.rootComponent
#                    
#                    for group in root.customGraphicsGroups:
#                        if group.id == 'BodySelect':
#                            group.deleteMe()
#
#                    group = root.customGraphicsGroups.add()
#                    group.id = 'BodySelect'
#                    graphicsBody = group.addBRepBody(fc.body)
#                    highlightColor = adsk.core.Color.create(200, 200, 200, 150)
#                    graphicsBody.color = adsk.fusion.CustomGraphicsSolidColorEffect.create(highlightColor)
#                    _app.activeViewport.refresh()
#                else:
#                    eventArgs.isSelectable = False
            elif typeList.selectedItem.name == 'Faces':
                if selInput.selectionCount > 0:
                    # Check the the plane are parallel.
                    existingFace = adsk.fusion.BRepFace.cast(selInput.selection(0).entity)
                    selectedFace = adsk.fusion.BRepFace.cast(eventArgs.selection.entity)
                    if existingFace.body == selectedFace.body:
                        (retVal, normal) = existingFace.evaluator.getNormalAtPoint(existingFace.pointOnFace)                
                        (retVal, selNormal) = selectedFace.evaluator.getNormalAtPoint(selectedFace.pointOnFace)
                        if selNormal.isParallelTo(normal):
                            eventArgs.isSelectable = True
                        else:
                            eventArgs.isSelectable = False
                    else:
                        eventArgs.isSelectable = False                
                else:
                     eventArgs.isSelectable = True
            elif typeList.selectedItem.name == 'Entire Sketch':
                if isinstance(eventArgs.selection.entity, adsk.fusion.SketchCurve):
                    skCurve = adsk.fusion.SketchCurve.cast(eventArgs.selection.entity)
                    moreEnts = adsk.core.ObjectCollection.create()
                    moreEnts.add(skCurve.parentSketch)
                    eventArgs.additionalEntities = moreEnts
                elif isinstance(eventArgs.selection.entity, adsk.fusion.Profile):
                    prof = adsk.fusion.Profile.cast(eventArgs.selection.entity)
                    moreEnts = adsk.core.ObjectCollection.create()
                    moreEnts.add(prof.parentSketch)
                    eventArgs.additionalEntities = moreEnts
                elif isinstance(eventArgs.selection.entity, adsk.fusion.SketchText):
                    skText = adsk.fusion.SketchText.cast(eventArgs.selection.entity)
                    moreEnts = adsk.core.ObjectCollection.create()
                    moreEnts.add(skText.parentSketch)
                    eventArgs.additionalEntities = moreEnts
            elif typeList.selectedItem.name =='Sketch Profiles':
                if selInput.selectionCount > 0:
                    existingProfile = adsk.fusion.Profile.cast(selInput.selection(0).entity)
                    if existingProfile.parentSketch == eventArgs.selection.entity.parentSketch:
                        eventArgs.isSelectable = True
                    else:
                        eventArgs.isSelectable = False
                else:
                    eventArgs.isSelectable = True
            elif typeList.selectedItem.name == 'Solid Bodies Layout':
                # Get the selected body.
                body = adsk.fusion.BRepBody.cast(eventArgs.selection.entity)

                # Determine if the selected body is oriented relative to the X-Y plane.
                if not isOrientedOnXYPlane(body):
                    eventArgs.isSelectable = False
                    return
            else:
                _ui.messageBox('test')
        except:
            if _logFile:
                _logFile.write('Failed in ExportCommandSelectionEventHandler')
                _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
                _logFile.flush()
                
            if _ui:
                _ui.messageBox('An unexpected error occurred in the Shaper Origin Export command.' +
                               '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                               adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


# Event handler for the activate event.
class ExportCommandActivateHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        global _firstActivate
        try:
            if _firstActivate:
                eventArgs = adsk.core.CommandEventArgs.cast(args)
        
                # Activate the visible selection.
#                listItem = adsk.core.ListItem.cast(None)
#                listItem = _typeList.listItems.item(0)
#                listItem.isSelected = True
                
                setSelectionState(_typeList.selectedItem.name)

                _firstActivate = False
        except:
            if _logFile:
                _logFile.write('Failed in ExportCommandActivateHandler')
                _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
                _logFile.flush()
                
            if _ui:
                _ui.messageBox('An unexpected error occurred in the Shaper Origin Export command.' +
                               '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                               adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


# Event handler for the inputChanged event.
class ExportCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
    
            if eventArgs.input.id == 'typeList':
                selItem = _typeList.selectedItem.name
                setSelectionState(selItem)
            elif eventArgs.input.id == 'Advanced':
                if _advanced.value:
                    _typeList.isVisible = True
                else:
                    _typeList.listItems.item(0).isSelected = True
                    setSelectionState('Single Solid Body')
                    _typeList.isVisible = False
                    _showOvercuts.isVisible = False
        except:                    
            if _logFile:
                _logFile.write('Failed in ExportCommandInputChangedHandler')
                _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
                _logFile.flush()
                
            if _ui:
                _ui.messageBox('An unexpected error occurred in the Shaper Origin Export command.' + 
                               '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                               adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


def setSelectionState(stateName):
    try:
#        if _isBeta and not _advanced.value:
#            _showOvercuts.isVisible = False
                  
        _selectInput.clearSelection()
        _selectInput.clearSelectionFilter()

        if stateName == 'Single Solid Body':
            _selectInput.addSelectionFilter('PlanarFaces') 
            _selectInput.setSelectionLimits(1,1)
            _textNotes.formattedText = _solidBodyPrompt
            _textNotes.numRows = 4
            
#            if _isBeta:
#                _showOvercuts.isVisible = True

        elif stateName =='Faces':
            _selectInput.addSelectionFilter('PlanarFaces')
            _selectInput.setSelectionLimits(1,0)
            _textNotes.formattedText = _facePrompt
            _textNotes.numRows = 4
        elif stateName =='Entire Sketch':
            _selectInput.addSelectionFilter('Sketches')
            _selectInput.addSelectionFilter('SketchCurves')
            _selectInput.addSelectionFilter('Texts')
            _selectInput.setSelectionLimits(1,1)
            _textNotes.formattedText = _sketchPrompt
            _textNotes.numRows = 3
        elif stateName =='Sketch Profiles':
            _selectInput.addSelectionFilter('Profiles')
            _selectInput.setSelectionLimits(1,0)
            _textNotes.formattedText = _profilePrompt
            _textNotes.numRows = 4
        elif stateName =='Solid Bodies Layout':
            _selectInput.addSelectionFilter('SolidBodies')
            _selectInput.setSelectionLimits(1,0)
            _textNotes.formattedText = _layoutsPrompt
            
#            if _isBeta:
#                _showOvercuts.isVisible = True
    except:                    
        if _logFile:
            _logFile.write('Failed in setSelectionState')
            _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
            _logFile.flush()
            
        if _ui:
            _ui.messageBox('An unexpected error occurred in the Shaper Origin Export command.' + 
                           '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                           adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


# Event handler for the execute event.
class ExportCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        orientedBody = None
        try:
            eventArgs = adsk.core.CommandEventArgs.cast(args)
            inputs = eventArgs.command.commandInputs
            
            # Save the current settings to use as the default.
            with open(os.path.join(_appPath, 'defaults.dat'), 'w') as file:
                advancedState = str(_advanced.value)
                typeState = _typeList.selectedItem.name
                file.write('advanced:' + advancedState + ';type:' + typeState  + ';beta:' + str(_isBeta) ) 

            des = adsk.fusion.Design.cast(_app.activeProduct)
            root = des.rootComponent
        
            # Get the inputs.
            showOverCuts = _showOvercuts.value

            filename = ''
    
            selItem = _typeList.selectedItem.name        
            if selItem == 'Single Solid Body':
                selection = _selectInput.selection(0)
                selectedFace = adsk.fusion.BRepFace.cast(selection.entity)
                body = selectedFace.body

                filename = body.name

                # Get a copy of the original body that is oriented flat onto 
                # the X-Y plane and create a Board object from it.
                (newOcc, orientedBody) = getOrientedBody(root, selectedFace)
                newBoard = MyBoard(orientedBody)
                newOcc.deleteMe()
                if not newBoard:
                    return

                allComposites = [newBoard.perimeterComposite]
                
                if newBoard.pocketComposites:
                    allComposites += newBoard.pocketComposites
                    
                if newBoard.internalThroughCuts:
                    allComposites += newBoard.internalThroughCuts
                
                compSets = []
                totalRange = None
                for comp in allComposites:
                    if not totalRange:
                        totalRange = comp.rangeBox
                    else:
                        totalRange.combine(comp.rangeBox)
                    
                compSets.append(allComposites)
                
                svgData = getSVG(allComposites, 'Title', 'Description', _version, totalRange, newBoard.thickness, showOverCuts)
            elif selItem =='Solid Bodies Layout':
                des = adsk.fusion.Design.cast(_app.activeProduct)                
                
                bodies = []
                for i in range(_selectInput.selectionCount):
                    bodies.append(_selectInput.selection(i).entity)

                # Check to see if there is a pending snapshot and capture it if there is.
                if des.designType == adsk.fusion.DesignTypes.ParametricDesignType:
                    if des.snapshots.hasPendingSnapshot:
                        des.snapshots.add()
                    
                # Check to see if any of the bodies come from a referenced component.
                refOccs = []
                currentDoc = _app.activeDocument
                for body in bodies:
                    parentComp = body.parentComponent
                    parentDes = parentComp.parentDesign
                    parentDoc = parentDes.parentDocument
                    
                    if parentDoc.name != currentDoc.name:
                        root = des.rootComponent
                        occs = root.allOccurrencesByComponent(parentDes.rootComponent)
                        
                        if occs.count > 0:
                            refOccs.append(occs.item(0))

                if len(refOccs) > 0:
                    _ui.messageBox('There is a current limitation that referenced components cannot be used. You must first break the link on the linked component in the browser and then run the "Export to Origin" command.\n\nYou can undo the break link after you have exported the SVG to maintain the link.')
                    eventArgs.executeFailed = True
                    return
                    
#                refOcc = adsk.fusion.Occurrence.cast(None)
#                for refOcc in refOccs:
#                    if refOcc.isReferencedComponent:
#                        refOcc.breakLink()
                                    
                # Construct the filename.
                filename = _app.activeProduct.rootComponent.name
                if not (filename[:1] == '(' and filename[len(filename)-1:len(filename)] == ')'):
                    filename = filename[:filename.rfind(' v')]
                filename = filename + '_layout'
    
                boardsCompositeSets = []
                boardThicknesses = []
                boardNames = []
                compSets = []
                totalRange = None
                for body in bodies:
                    newBoard = MyBoard(body)
                    boardNames.append(body.name)
                    boardThicknesses.append(newBoard.thickness)

                    # Initialize the list with the perimeter composite.
                    boardComposites = [newBoard.perimeterComposite]

                    # Add the pockets, if there are any.
                    if newBoard.pocketComposites:
                        boardComposites += newBoard.pocketComposites
                        
                    # Add the internal cutouts, if there are any.
                    if newBoard.internalThroughCuts:
                        boardComposites += newBoard.internalThroughCuts                   

                    boardsCompositeSets.append(boardComposites)

                for boardList in boardsCompositeSets:
                    for comp in boardList:
                        if not totalRange:
                            totalRange = comp.rangeBox
                        else:
                            totalRange.combine(comp.rangeBox)

                svgData = getLayoutSVG(boardsCompositeSets, 'Title', 'Description', _version, totalRange, boardThicknesses, boardNames, showOverCuts)
            elif selItem =='Faces':
                faces = []
                for i in range(_selectInput.selectionCount):
                    faces.append(_selectInput.selection(i).entity)
                    
                body = faces[0].body
                (newOcc, orientedBody) = getOrientedBody(root, faces[0])
                
                filename = body.name + '_Faces'
                
                faceIndices = []
                indexCount = 0
                for testFace in body.faces:
                    for face in faces:
                        if face == testFace:
                            faceIndices.append(indexCount)
                    
                    indexCount += 1

                faces.clear()
                for faceIndex in faceIndices:
                    faces.append(orientedBody.faces.item(faceIndex))

                composites = createCompositesFromFaces(faces)
                newOcc.deleteMe()

                totalRange = None
                for comp in composites:
                    if not totalRange:
                        totalRange = comp.rangeBox
                    else:
                        totalRange.combine(comp.rangeBox)
                
                svgData = getSVG([composites], 'Title', 'Description', _version, totalRange, 0, False)                
            elif selItem =='Entire Sketch':
                for i in range(_selectInput.selectionCount):
                    if isinstance(_selectInput.selection(i).entity, adsk.fusion.Sketch):
                        sk = _selectInput.selection(i).entity

                filename = sk.name
                
                composites = createCompositesFromSketch(sk, True)

                totalRange = None
                for comp in composites:
                    if not totalRange:
                        totalRange = comp.rangeBox
                    else:
                        totalRange.combine(comp.rangeBox)

                svgData = getSVG([composites], 'Title', 'Description', _version, totalRange, 0, False)
            elif selItem =='Sketch Profiles':
                if True:
                    # Original code that creates output for selected profiles.
                    profiles = []
                    for i in range(_selectInput.selectionCount):
                        profiles.append(_selectInput.selection(i).entity)
                        
                    filename = profiles[0].parentSketch.name + '_Profiles'
                    
                    composites = createCompositesFromProfiles(profiles)
                else:
                    # Code that finds all profiles whose name begins with "Cut"
                    # and uses them.  This was hacked together for the star.
                    selProf = adsk.fusion.Profile.cast(_selectInput.selection(0).entity)
                    profiles = []
                    sk = adsk.fusion.Sketch.cast(None)
                    for sk in root.sketches:
                        if sk.name.startswith('Cut'):
                            prof = sk.profiles.item(0)
                            profiles.append(prof)
                        
                    filename = 'CutSketches_Profiles'
                    
                    composites = createCompositesFromProfiles(profiles, True)

                totalRange = None
                for comp in composites:
                    if not totalRange:
                        totalRange = comp.rangeBox
                    else:
                        totalRange.combine(comp.rangeBox)

                svgData = getSVG([composites], 'Title', 'Description', _version, totalRange, 0, False)

#            if newOcc:
#                newOcc.deleteMe()
            
            fileDialog = _ui.createFileDialog()
            fileDialog.isMultiSelectEnabled = False
            fileDialog.title = "Specify output filename"
            fileDialog.filter = 'SVG files (*.svg)'
            fileDialog.filterIndex = 0
            fileDialog.initialFilename = filename + '.svg'
            dialogResult = fileDialog.showSave()
            if dialogResult == adsk.core.DialogResults.DialogOK:
                filename = fileDialog.filename
            else:
                eventArgs.executeFailed = True
                return
    
            with open(filename, 'w') as text_file:
                text_file.write(svgData)

            eventArgs.executeFailed = True
        except:              
            eventArgs.executeFailed = True
            
            if orientedBody:
                orientedBody.isVisible = False

            if _logFile:
                _logFile.write('Failed in ExportCommandExecuteHandler')
                _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
                _logFile.flush()
                
            if _ui:
                _ui.messageBox('An unexpected error occurred while executing the Shaper Origin Export command.' +
                               '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                               adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


# Determines if the input body is oriented so it is lying on the X-Y plane.
def isOrientedOnXYPlane(body):
    try:
        # As a first check, verify that the X-Y size of the bounding
        # box is larger than the Z size.
        b = adsk.fusion.BRepBody.cast(body)
        xSize = b.boundingBox.maxPoint.x - b.boundingBox.minPoint.x
        ySize = b.boundingBox.maxPoint.y - b.boundingBox.minPoint.y
        zSize = b.boundingBox.maxPoint.z - b.boundingBox.minPoint.z

        xyDiag = math.sqrt(math.pow(xSize,2) + math.pow(ySize,2))
        if xyDiag < zSize:
            return False
            
        # Find the planar face with the largest area.
        maxArea = 0
        maxFace = None
        face = adsk.fusion.BRepFace.cast(None)
        for face in b.faces:
            if face.geometry.surfaceType == adsk.core.SurfaceTypes.PlaneSurfaceType:
                if face.area > maxArea:
                    maxArea = face.area
                    maxFace = face
            
        # Assuming the found face is either the top or bottom face, get
        # it's normal and verify that it is parallel to the Z axis.
        if maxFace:
            (retVal, normal) = maxFace.evaluator.getNormalAtPoint(maxFace.pointOnFace)
            if normal.isParallelTo(adsk.core.Vector3D.create(0,0,1)):
                return True
            else:
                return False
        else:
            return False
    except:
        if _logFile:
            _logFile.write('Failed in isOrientedOnXYPlane')
            _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
            _logFile.flush()

        raise     


def getOrientedBody(root, upperFace):
    try:  
        # Get the body from the input face.
        originalBody = upperFace.body

        # Create a new occurrence (and component).
        trans = adsk.core.Matrix3D.create()
        occ = adsk.fusion.Occurrence.cast(root.occurrences.addNewComponent(trans))     

        # Copy the body into the new component.
        body = originalBody.copyToComponent(occ)

        # Use the normal of the upper face as the Z direction.
        zVec = adsk.core.Vector3D.cast(None)
        (retVal, zVec) = upperFace.evaluator.getNormalAtPoint(upperFace.pointOnFace)

        # Find the longest linear edge of the body that is more than 45 degrees from the "up" direction.
        maxLength = 0
        maxEdge = None
        edge = adsk.fusion.BRepEdge.cast(None)
        for edge in body.edges:
            if edge.geometry:
                if edge.geometry.objectType == adsk.core.Line3D.classType():
                    lineGeom = adsk.core.Line3D.cast(edge.geometry)
                    lineVec = lineGeom.startPoint.vectorTo(lineGeom.endPoint)
                    lineAngle = lineVec.angleTo(zVec)
                    
                    if lineAngle > (math.pi * 0.25) and lineAngle < (math.pi * 0.75): 
                        if edge.length > maxLength:
                            maxLength = edge.length
                            maxEdge = edge

        if maxEdge:
            # Check length of edge relative to the bounding box size along the major axes.
            # If the edge is less than 30% of a major axis, then use which ever major axis
            # that is perpendicular to the upper face normal that is the longest.
            boundX = body.boundingBox.maxPoint.x - body.boundingBox.minPoint.x
            boundY = body.boundingBox.maxPoint.y - body.boundingBox.minPoint.y
            boundZ = body.boundingBox.maxPoint.z - body.boundingBox.minPoint.z
    
            # Initialize the values assuming the X direction is closest to the normal direction.
            startPnt = body.boundingBox.minPoint.copy()        
            normalAngle = adsk.core.Vector3D.create(1,0,0).angleTo(zVec)
            if boundY > boundZ:
                xVec = adsk.core.Vector3D.create(0,1,0)
                boundMax = boundY
                endPnt = startPnt.copy()
                endPnt.y += boundMax
            else:
                xVec = adsk.core.Vector3D.create(0,0,1)
                boundMax = boundZ
                endPnt = startPnt.copy()
                endPnt.z += boundMax
            
            # Check to see of the Y direction is closest to the face normal.        
            testAngle = adsk.core.Vector3D.create(0,1,0).angleTo(zVec)
            if testAngle < normalAngle:
                normalAngle = testAngle
        
                if boundX > boundZ:
                    xVec = adsk.core.Vector3D.create(1,0,0)
                    boundMax = boundX
                    endPnt = startPnt.copy()
                    endPnt.x += boundMax
                else:
                    xVec = adsk.core.Vector3D.create(0,0,1)
                    boundMax = boundZ
                    endPnt = startPnt.copy()
                    endPnt.z += boundMax
                
            # Check to see of the Z direction is closest to the face normal.        
            testAngle = adsk.core.Vector3D.create(0,0,1).angleTo(zVec)
            if testAngle < normalAngle:
                normalAngle = testAngle
    
                if boundX > boundY:
                    xVec = adsk.core.Vector3D.create(1,0,0)
                    boundMax = boundX
                    endPnt = startPnt.copy()
                    endPnt.x += boundMax
                else:
                    xVec = adsk.core.Vector3D.create(0,1,0)
                    boundMax = boundY
                    endPnt = startPnt.copy()
                    endPnt.y += boundMax
    
            # Check to see if the longest edge is greater than 30% of the longest bounds.
            if maxLength < boundMax * 0.3:                    
                # Use the bounding box direction.
                xVec.normalize()
                yVec = zVec.crossProduct(xVec)
                
                startPnt = maxEdge.startVertex.geometry
                endPoint = maxEdge.endVertex.geometry
            else:
                # Use the found edge.
                xVec = maxEdge.startVertex.geometry.vectorTo(maxEdge.endVertex.geometry)
                xVec.normalize()
                yVec = zVec.crossProduct(xVec)
                startPnt = maxEdge.startVertex.geometry
                endPoint = maxEdge.endVertex.geometry
                
            midPoint = adsk.core.Point3D.create((startPnt.x + endPoint.x)/2, (startPnt.y + endPoint.y)/2, (startPnt.z + endPoint.z)/2)
            testVec = midPoint.vectorTo(upperFace.pointOnFace)
            origin = maxEdge.startVertex.geometry
            if yVec.angleTo(testVec) > math.pi/2:
                xVec.scaleBy(-1)
                yVec = zVec.crossProduct(xVec)
                origin = maxEdge.endVertex.geometry
            
            yVec.normalize()
        else:
            # There aren't any linear edges so create a coordinate system that uses the
            # Z axis of the upper face and finds the model axes that most closely align.
            if zVec.angleTo(adsk.core.Vector3D.create(1,0,0)) <= math.pi * 0.25:
                xVec = adsk.core.Vector3D.create(0,1,0)
                yVec = adsk.core.Vector3D.create(0,0,1)
            elif zVec.angleTo(adsk.core.Vector3D.create(-1,0,0)) <= math.pi * 0.25:
                xVec = adsk.core.Vector3D.create(0,1,0)
                yVec = adsk.core.Vector3D.create(0,0,1)
            elif zVec.angleTo(adsk.core.Vector3D.create(0,1,0)) <= math.pi * 0.25:
                xVec = adsk.core.Vector3D.create(-1,0,0)
                yVec = adsk.core.Vector3D.create(0,0,1)
            elif zVec.angleTo(adsk.core.Vector3D.create(0,-1,0)) <= math.pi * 0.25:
                xVec = adsk.core.Vector3D.create(1,0,0)
                yVec = adsk.core.Vector3D.create(0,0,1)
            elif zVec.angleTo(adsk.core.Vector3D.create(0,0,1)) <= math.pi * 0.25:
                xVec = adsk.core.Vector3D.create(1,0,0)
                yVec = adsk.core.Vector3D.create(0,1,0)
            elif zVec.angleTo(adsk.core.Vector3D.create(0,0,-1)) <= math.pi * 0.25:
                xVec = adsk.core.Vector3D.create(-1,0,0)
                yVec = adsk.core.Vector3D.create(0,1,0)

            yVec = zVec.crossProduct(xVec)
            xVec = yVec.crossProduct(zVec)  
            origin = upperFace.pointOnFace
        
        trans.setWithCoordinateSystem(origin, xVec, yVec, zVec)
        trans.invert()
        currentTrans = occ.transform
        currentTrans.transformBy(trans)
        occ.transform = currentTrans
        
        des = adsk.fusion.Design.cast(root.parentDesign)
        if des.designType == adsk.fusion.DesignTypes.ParametricDesignType:
            if des.snapshots.hasPendingSnapshot:
                des.snapshots.add()
        
        return [occ, body]
    except:
        if _logFile:
            _logFile.write('Failed in getOrientedBody')
            _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
            _logFile.flush()

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

        try:
            tempDir = tempfile.gettempdir()
            filename = os.path.join(tempDir, 'ShaperUtilities.log')
            _logFile = open(filename, 'w')
            setLogFile(_logFile)
        except:
            _logFile = None
        
        # Create the command to export SVG.
        exportCmdDef = _ui.commandDefinitions.addButtonDefinition('shaperExport', 'Export to Origin', 'Export the selected body(s) to a file that can be used by Shaper Origin.', 'resources/shaperExport')
        
        # Connect to the command created event.
        exportCommandCreated = ExportCommandCreatedEventHandler()
        exportCmdDef.commandCreated.add(exportCommandCreated)
        _handlers.append(exportCommandCreated)
        
        # Get the MODEL workspace.
        modelWS = _ui.workspaces.itemById('FusionSolidEnvironment')
        
        # Add a new SHAPER panel.
        shaperPanel = modelWS.toolbarPanels.add('shaperPanel', 'SHAPER', 'SolidScriptsAddinsPanel', False)
        
        # Add the buttons to the panel.
        exportCntrl = shaperPanel.controls.addCommand(exportCmdDef)
        exportCntrl.isPromotedByDefault = True
        exportCntrl.isPromoted = True
    except:
        if _logFile:
            _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
            _logFile.flush()
            
        if _ui:
            _ui.messageBox('An unexpected error occurred while loading the Shaper Utilities add-in.'
                           '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                           adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)


def stop(context):
    try:
        panel = _ui.allToolbarPanels.itemById('shaperPanel')
        if panel:
            panel.deleteMe()
            
        exportCmdDef = _ui.commandDefinitions.itemById('shaperExport')
        if exportCmdDef:
            exportCmdDef.deleteMe()

    except:
        if _logFile:
            _logFile.write('Failed:\n{}'.format(traceback.format_exc()))
            _logFile.flush()

        if _ui:
            _ui.messageBox('An unexpected error occurred while unloading the Shaper Utilities add-in.'
                           '\n\nAn error log is available in:\n"' + _logFile.name + '".', 'Shaper Origin Export',
                           adsk.core.MessageBoxButtonTypes.OKButtonType, adsk.core.MessageBoxIconTypes.CriticalIconType)

Tested under Windows 7.

 

Tags (3)
Message 6 of 12
plowry01
in reply to: JeromeBriot

Thankyou greatly, it worked.

 

For the other noobs like me, 

 

Open file explorer

paste "ShaperUtilities.py" into the search bar at the top right

right click ShaperUtilities.py and open in notepad

select all, and replace the code with code Jerome gave above

save

reopen fusion and it should be there.

Message 7 of 12
edgerton.mit
in reply to: plowry01

Worked for me too!

Thanks for the clear and easy instructions!

Message 8 of 12
Anonymous
in reply to: JeromeBriot

Worked for me as well! Thanks!

Liam

Message 9 of 12
keenanpm
in reply to: plowry01

Can someone tell me where the Shaper Origin is supposed to show up in the new UI?  I have tried adding the code mentioned, and can't find the plugin.

Message 10 of 12
Anonymous
in reply to: keenanpm

any luck with this. late to the party.... trying to install on Mac. install seemed to go ok but shaper plug-in not showing up in fusion. anyone?

Message 11 of 12
JeromeBriot
in reply to: keenanpm

@keenanpm and @Anonymous, the add-in should appear in the TOOLS panel:

shaper-fusion-360-toolbar.png

Message 12 of 12
Anonymous
in reply to: JeromeBriot

That was the answer. Solved. Thank you very much.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums