-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 19c720f
Showing
64 changed files
with
850 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/.idea | ||
/.vscode | ||
/__pycache__ | ||
/defaults.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"autodeskProduct": "Fusion360", | ||
"type": "addin", | ||
"id": "51e13efe-8453-49c7-becd-bfed6571c1cf", | ||
"author": "Florian Pommerening", | ||
"description": { | ||
"": "An add-in for making finger joints." | ||
}, | ||
"version": "1.0.0", | ||
"runOnStartup": true, | ||
"supportedOS": "windows|mac", | ||
"editEnabled": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#Author-Florian Pommerening | ||
#Description-An Add-In for making finger joints. | ||
|
||
# Select two overlapping bodies and a direction. The overlap is cut along the | ||
# direction multiple times resulting in the individual fingers/notches. We | ||
# then remove every second finger from the first body and the other fingers | ||
# from the second body. The remaining bodies then do not overlap anymore. | ||
|
||
# Some inspiration was taken from the dogbone add-in developed by Peter | ||
# Ludikar, Gary Singer, Patrick Rainsberry, David Liu, and Casey Rogers. | ||
|
||
import adsk.core | ||
import adsk.fusion | ||
|
||
from . import commands | ||
from . import options | ||
from . import ui | ||
from . import geometry | ||
|
||
|
||
# Global variable to hold the add-in (created in run(), destroyed in stop()) | ||
addin = None | ||
|
||
|
||
class FingerJointCommand(commands.RunningCommandBase): | ||
def __init__(self, args: adsk.core.CommandCreatedEventArgs): | ||
super(FingerJointCommand, self).__init__(args) | ||
self.options = options.FingerJointOptions() | ||
self.ui = ui.FingerJointUI(args.command.commandInputs, self.options) | ||
|
||
def onInputChanged(self, args: adsk.core.InputChangedEventArgs): | ||
self.ui.updateVisibility() | ||
self.ui.focusNextSelectionInput() | ||
|
||
def onValidate(self, args: adsk.core.ValidateInputsEventArgs): | ||
args.areInputsValid = self.ui.areInputsValid() | ||
|
||
def onExecutePreview(self, args: adsk.core.CommandEventArgs): | ||
if self.ui.isPreviewEnabled(): | ||
if self.doExecute(): | ||
args.isValidResult = True | ||
self.ui.setInputErrorMessage('') | ||
else: | ||
self.ui.setInputErrorMessage('Finger joints could not be completed. Try selecting overlapping bodies and double-check the dimensions.') | ||
|
||
def onExecute(self, args: adsk.core.CommandEventArgs): | ||
if not self.doExecute(): | ||
args.executeFailed = True | ||
args.executeFailedMessage = 'Finger joints could not be completed. Try selecting overlapping bodies and double-check the dimensions.' | ||
|
||
def doExecute(self): | ||
self.ui.setRelevantOptions(self.options) | ||
body0 = self.ui.getBody0() | ||
body1 = self.ui.getBody1() | ||
direction = self.ui.getDirection() | ||
return geometry.createFingerJoint(body0, body1, direction, self.options) | ||
|
||
def onDestroy(self, args: adsk.core.CommandEventArgs): | ||
super(FingerJointCommand, self).onDestroy(args) | ||
if args.terminationReason == adsk.core.CommandTerminationReason.CompletedTerminationReason: | ||
self.options.writeDefaults() | ||
|
||
|
||
class FingerJointAddIn(object): | ||
def __init__(self): | ||
self.button = commands.CommandButton('FingerJointBtn', 'SolidModifyPanel', FingerJointCommand) | ||
|
||
def start(self): | ||
self.button.addToUI('Finger Joint', | ||
'Creates a finger joint from the overlap of two bodies', | ||
'resources/ui/command_button') | ||
|
||
def stop(self): | ||
self.button.removeFromUI() | ||
|
||
|
||
def run(context): | ||
global addin | ||
try: | ||
if addin is not None: | ||
stop() | ||
addin = FingerJointAddIn() | ||
addin.start() | ||
except: | ||
ui.reportError('Uncaught exception', True) | ||
|
||
|
||
def stop(context): | ||
global addin | ||
addin.stop() | ||
addin = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Finger Joints | ||
|
||
This add-in for Autodesk Fusion 360 can create a finger joint (box joint) from the overlap of two objects. Although, not specifically designed for this, the add-in also works for lap joints and can cut pieces into slices. | ||
|
||
![Finger joint](resources/doc/demo.png) | ||
|
||
|
||
## Installation | ||
|
||
Download the [latest version of the plugin](https://github.com/FlorianPommerening/FingerJoints/archive/master.zip), unpack it to your add-ins directory (see below) and remove the "-master" from the name. To start the add-in inside Fusion use the "ADD-INS" button in the "TOOLS" ribbon, then find the add-in in the "Add-Ins" tab, select it and click "Run". | ||
|
||
Add-ins directory | ||
|
||
* Windows: `%AppData%\Autodesk\Autodesk Fusion 360\API\AddIns` | ||
* Mac OS X: `~/Library/Application Support/Autodesk/Autodesk Fusion 360/API/AddIns` | ||
or ` ~/Library/Containers/com.autodesk.mas.fusion360/Data/Library/Application Support/Autodesk/Autodesk Fusion 360/API/AddIns` | ||
|
||
There are also [more detailed installation instructions](https://tapnair.github.io/installation.html) by Patrick Rainsberry. | ||
|
||
|
||
## Usage | ||
|
||
Select two overlapping bodies and a direction. The direction is required, so the add-in knows in which plane to cut. In most cases, you probably want an edge that runs along the joint as shown in the picture. | ||
|
||
![Selection of body and edges](resources/doc/usage1.png) | ||
|
||
The next setting controls whether the first body has fingers on both ends, notches on both ends, or a finger on one end and a notch on the other. In the next picture, we place three fingers on the first body with different settings, the number of fingers on the second body adjusts accordingly. | ||
|
||
![Finger placement](resources/doc/usage2.png) | ||
|
||
The next settings determine how the size of the overlap is distributed between fingers of the first body and fingers of the second body (as we view everything from the perspective of the first body, we call fingers of the second body "notches"). | ||
|
||
When the number of fingers is fixed, we can specify their size in three ways. In the example above, we chose to have fingers and notches of equal size. Instead, we can chose to fix the size of either fingers or notches (the other size is determined automatically). The next picture shows the effect of fixing the size of fingers or notches to 5mm. | ||
|
||
![Fixed finger size with fixed number of fingers](resources/doc/usage3.png) | ||
|
||
Finally, instead of fixing the number of fingers, we might want to have both fingers and notches of a certain size and place as many fingers as will fit along the joint. Fixing both sizes could lead to cases where one body ends in half a finger, so instead, we only fix either the size of fingers or notches and specify a minimal size for the other. For example, the following picture fixes the size of a finger to 9mm and uses a minimal notch size of 5mm. The height of the joint is 50mm, so using four 9mm fingers and three 5mm notches would exceed the size by 1 mm. Instead, the add-in uses three 9mm fingers and increases the size of the two notches to 11.5mm. Likewise, we could fix the size of notches and make the size of fingers dynamic, or we could require that fingers and notches have the same size. | ||
|
||
![Dynamic finger number with fixed finger size](resources/doc/usage4.png) | ||
|
||
|
||
Of course, the objects you join do not have to be rectangular and can overlap in multiple places. | ||
|
||
![General objects can be joined](resources/doc/usage5.png) | ||
![Resulting objects](resources/doc/usage6.png) | ||
|
||
|
||
## Other Uses | ||
|
||
In addition to creating finger joints, the add-in can be used to create lap joints and cut pieces into slices. | ||
|
||
### Lap Joints | ||
|
||
To create a lap joint, start with two overlapping pieces and create a finger joint with one finger placed asymmertically on the start or end of the first body. | ||
|
||
![Lap joint settings](resources/doc/lapjointssettings.png) | ||
![Lap joint result](resources/doc/lapjointsresults.png) | ||
|
||
### Slicing | ||
|
||
This is not really by design but you can duplicate a body so it perfectly overlaps with itself, then create a finger joint between the | ||
copy and the original. This will slice the body along your chosen axis. | ||
|
||
![Slicing](resources/doc/slicing1.png) | ||
![Slicing](resources/doc/slicing2.png) | ||
|
||
|
||
## Issues | ||
|
||
If you find any issues with the add-in, please report them on [Github][issuetracker] | ||
|
||
## TODO / Help wanted | ||
|
||
If you have ideas how to improve the add-in, you can create an issue on [Github][issuetracker]. Since I am working on this in my free time I cannot promise I will get to it, though. Pull requests for fixes and new features are very welcome. | ||
|
||
The add-in is currently not parametric, so updating the underlying geometry will not update the placement or size of the fingers. I'm not sure this is possible with the current API (there is a [related request on the Fusion 360 IdeaStation][ideastation]). If you have an idea of how to implement this, please get in touch. | ||
|
||
I am not an artist, so the icons are still [Programmer art](https://en.wikipedia.org/wiki/Programmer_art). I'd be happy to replace them with something closer to the style of the built-in commands. | ||
|
||
## License | ||
|
||
[![Creative Commons License][by-nc-sa-logo]][by-nc-sa-link] | ||
|
||
This add-in is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][by-nc-sa-link]. If you are interested in using the add-in under different terms, contact florian.pommerening@unibas.ch. | ||
|
||
|
||
[by-nc-sa-logo]: https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png | ||
[by-nc-sa-link]: http://creativecommons.org/licenses/by-nc-sa/4.0/ | ||
[issuetracker]: https://github.com/FlorianPommerening/FingerJoints/issues | ||
[ideastation]: https://forums.autodesk.com/t5/fusion-360-ideastation/allow-add-ins-to-be-fully-parametric-and-represented-in-the/idi-p/8660436 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import adsk.core | ||
import adsk.fusion | ||
|
||
from . import ui | ||
|
||
# Keep track of all currently running commands in a global set, so their | ||
# callback handlers are not GC'd. | ||
running_commands = set() | ||
|
||
class RunningCommandBase(object): | ||
""" | ||
Base class to keep persistent data during the lifetime of a command from | ||
creation to destruction. The constructor of this class automatically adds | ||
the instance to running_commands and the onDestroy event removes it again. | ||
To use this class, inherit from it an override the events. | ||
""" | ||
|
||
def __init__(self, args): | ||
running_commands.add(self) | ||
|
||
cmd = adsk.core.Command.cast(args.command) | ||
|
||
self._inputChangedHandler = makeForwardingHandler( | ||
adsk.core.InputChangedEventHandler, self.onInputChanged) | ||
cmd.inputChanged.add(self._inputChangedHandler) | ||
|
||
self._selectionHandler = makeForwardingHandler( | ||
adsk.core.SelectionEventHandler, self.onSelectionEvent) | ||
cmd.selectionEvent.add(self._selectionHandler) | ||
|
||
self._validateHandler = makeForwardingHandler( | ||
adsk.core.ValidateInputsEventHandler, self.onValidate) | ||
cmd.validateInputs.add(self._validateHandler) | ||
|
||
self._executeHandler = makeForwardingHandler( | ||
adsk.core.CommandEventHandler, self.onExecute) | ||
cmd.execute.add(self._executeHandler) | ||
|
||
self._executePreviewHandler = makeForwardingHandler( | ||
adsk.core.CommandEventHandler, self.onExecutePreview) | ||
cmd.executePreview.add(self._executePreviewHandler) | ||
|
||
self._destroyHandler = makeForwardingHandler( | ||
adsk.core.CommandEventHandler, self.onDestroy) | ||
cmd.destroy.add(self._destroyHandler) | ||
|
||
def onCreated(self, args): | ||
pass | ||
|
||
def onInputChanged(self, args): | ||
pass | ||
|
||
def onSelectionEvent(self, args): | ||
pass | ||
|
||
def onValidate(self, args): | ||
pass | ||
|
||
def onExecute(self, args): | ||
pass | ||
|
||
def onExecutePreview(self, args): | ||
pass | ||
|
||
def onDestroy(self, args): | ||
running_commands.remove(self) | ||
|
||
|
||
class CommandCreatedHandler(adsk.core.CommandCreatedEventHandler): | ||
def __init__(self, runningCommandClass): | ||
super().__init__() | ||
self.runningCommandClass = runningCommandClass | ||
|
||
def notify(self, args): | ||
try: | ||
running_command = self.runningCommandClass(args) | ||
running_command.onCreated(args) | ||
except: | ||
ui.reportError('Command creation callback method failed', True) | ||
|
||
|
||
def makeForwardingHandler(handler_cls, callback): | ||
class ForwardingHandler(handler_cls): | ||
def __init__(self, callback): | ||
super().__init__() | ||
self.callback = callback | ||
|
||
def notify(self, args): | ||
try: | ||
self.callback(args) | ||
except: | ||
ui.reportError('Callback method failed', True) | ||
return ForwardingHandler(callback) | ||
|
||
|
||
class CommandButton(object): | ||
def __init__(self, commandID, panelName, commandDataClass): | ||
self.commandID = commandID | ||
self.panelName = panelName | ||
|
||
fusion = adsk.core.Application.get() | ||
self.fusionUI = fusion.userInterface | ||
self.creationHandler = CommandCreatedHandler(commandDataClass) | ||
|
||
def addToUI(self, name, tooltip='', resourceFolder=''): | ||
# If there are existing instances of the button, clean them up first. | ||
try: | ||
self.removeFromUI() | ||
except: | ||
pass | ||
|
||
# Create a command definition and button for it. | ||
commandDefinition = self.fusionUI.commandDefinitions.addButtonDefinition( | ||
self.commandID, name, tooltip, resourceFolder) | ||
commandDefinition.commandCreated.add(self.creationHandler) | ||
|
||
panel = self.fusionUI.allToolbarPanels.itemById(self.panelName) | ||
buttonControl = panel.controls.addCommand(commandDefinition) | ||
|
||
# Make the button available in the panel. | ||
buttonControl.isPromotedByDefault = True | ||
buttonControl.isPromoted = True | ||
|
||
def removeFromUI(self): | ||
commandDefinition = self.fusionUI.commandDefinitions.itemById(self.commandID) | ||
if commandDefinition: | ||
commandDefinition.deleteMe() | ||
panel = self.fusionUI.allToolbarPanels.itemById(self.panelName) | ||
buttonControl = panel.controls.itemById(self.commandID) | ||
if buttonControl: | ||
buttonControl.deleteMe() |
Oops, something went wrong.