Thank you for your answer!
It is good to have someone confirm that no amount of hacky python code will let me mess with the inbuilt commands.
By now I have just opted to make the Add-Ins workflow a tad more tedious by making creating the joint origins a prerequisite. Howver, in case it is relevant to anyone else in the future, some general findings:
Replicating the snapping behaviour of the Joint and JointOrigin commands does not seem to be possible, even when using CustomGraphics. Apart from the large amount of effort needed to replicate the orientation being dependent on the relative mouse position as well as the actual snapping, it also isn't possible to select CustomGraphics created during the selection process.
Creating the CustomGraphics during a PreSelect hook works just fine, and the user interface also shows them as aligible for selection. However on clicking them, the CustomGraphics get removed and no selection is made. This seems to be inline with all CustomGraphics created during selection also being removed upon canceling/ending the selection process in general.
This seems to be intentional, as in contrast, CustomGraphics that have previously been created in an "execute" Method can be properly selected, so the issue does seem to be them being created during the preSelect. Probably this is some intentional lifecycle thing, I found a post where someone clarified they are also only intended for display purposes.
It would be nice though if someone could confirm that CustomGraphics created during any of the Selection hooks can not be selected themselves.
Here is a MWE adapted from the Add-In default template that illustrates this, just in case it isn't intentional/someone wants to tinker with it.
import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config
app = adsk.core.Application.get()
ui = app.userInterface
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_type_joint'
CMD_NAME = 'Snap Point Example'
CMD_Description = 'Illustrate custom graphics behaviour.'
IS_PROMOTED = True
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'TYPES'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'
# Resource location
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')
local_handlers = []
def start():
# Command Definition.
cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)
futil.add_handler(cmd_def.commandCreated, command_created)
# Register
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
control2 = panel.controls.addCommand(ui.commandDefinitions.itemById("JointOrigin"))
control2.isPromoted = True
control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)
control.isPromoted = IS_PROMOTED
# Executed when add-in is stopped.
def stop():
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
command_definition = ui.commandDefinitions.itemById(CMD_ID)
for i in range(panel.controls.count):
if panel.controls.item(0):
panel.controls.item(0).deleteMe()
# Delete the command definition
if command_definition:
command_definition.deleteMe()
def command_created(args: adsk.core.CommandCreatedEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Created Event')
# Handlers
futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)
#futil.add_handler(args.command.preSelectMouseMove, command_preSelectMouseMove, local_handlers=local_handlers)
futil.add_handler(args.command.preSelect, command_preSelect, local_handlers=local_handlers)
futil.add_handler(args.command.preSelectEnd, command_preSelectEnd, local_handlers=local_handlers)
futil.add_handler(args.command.executePreview, command_executePreview, local_handlers=local_handlers)
futil.add_handler(args.command.select, command_select, local_handlers=local_handlers)
inputs = args.command.commandInputs
# UI DEF
selectionInput = inputs.addSelectionInput('selection', 'Select', 'Select')
selectionInput.setSelectionLimits(0)
def command_executePreview(args: adsk.core.CommandEventHandler):
app = adsk.core.Application.get()
def command_select(args: adsk.core.SelectionEventArgs):
app = adsk.core.Application.get()
def command_preSelectMouseMove(args: adsk.core.SelectionEventArgs):
app = adsk.core.Application.get()
def command_preSelect(args: adsk.core.SelectionEventArgs):
selectedFace = adsk.fusion.BRepFace.cast(args.selection.entity)
design = adsk.fusion.Design.cast(app.activeProduct)
graphics = design.rootComponent.customGraphicsGroups.add()
if selectedFace:
coord_array = [selectedFace.centroid.x,selectedFace.centroid.y,selectedFace.centroid.z]
coords = adsk.fusion.CustomGraphicsCoordinates.create(coord_array)
points = graphics.addPointSet(coords, [0],
adsk.fusion.CustomGraphicsPointTypes.UserDefinedCustomGraphicsPointType,
ICON_FOLDER + '/64x64.png')
tmatrix = adsk.core.Matrix3D.create()
#if args.selection.entity.classType() != adsk.fusion.CustomGraphicsPointSet.classType() and args.selection.entity.classType() != adsk.fusion.BRepFace.classType():
# Only allow Faces or CustomGraphics
# args.isSelectable = False
def command_preSelectEnd(args: adsk.core.SelectionEventArgs):
app = adsk.core.Application.get()
# EXECUTE
def command_execute(args: adsk.core.CommandEventArgs):
# General logging for debug
futil.log(f'{CMD_NAME} Command Execute Event')
def command_preview(args: adsk.core.CommandEventArgs):
inputs = args.command.commandInputs
futil.log(f'{CMD_NAME} Command Preview Event')
def command_input_changed(args: adsk.core.InputChangedEventArgs):
changed_input = args.input
inputs = args.inputs
futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')
def command_destroy(args: adsk.core.CommandEventArgs):
global local_handlers
local_handlers = []
futil.log(f'{CMD_NAME} Command Destroy Event')