How to use Triad for for simple user-controlled Scaling

How to use Triad for for simple user-controlled Scaling

goutam_reddy
Participant Participant
800 Views
5 Replies
Message 1 of 6

How to use Triad for for simple user-controlled Scaling

goutam_reddy
Participant
Participant

Hi,

 

I'm using the API to write an add-in that (at one point) lets the user scale a selected body.

I've been able to use the "addTriadCommandInput" with success for translation and rotation of bodies.

However, for scaling there are a few things I can't seem to get working and I don't understand:

1) When we set the triad parameters to "inputs.itemById('scale_triad').setScaleVisibility(True)", I would have expected 3 arrows in the X, Y, Z axes that a user could use to "pull" to stretch/shrink the object along those axes.

However, instead there are a bunch of controls for scaling in different planes.  These get set automatically to true:

<isXScalingInXYVisible? True

isXScalingInXZVisible? True

isYScalingInXYVisible? True

isYScalingInYZVisible? True

isZScalingInXZVisible? True

isZScalingInYZVisible? True

isXYPlaneScalingVisible? True

isXZPlaneScalingVisible? True

isYZPlaneScalingVisible? True>

2) When pulling on these "planar" scaling options, they do change the values for the "xyPlaneScaleFactor", but do not change the underlying transform.

Screenshot 2023-01-10 at 8.35.33 PM.png

"

transform value as array: (1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0)

xScaleFactor: 1.0

yScaleFactor: 1.0

zScaleFactor: 1.0

xYPlaneScaleFactor: 1.7420363507829468

yZPlaneScaleFactor: 1.0

zXPlaneScaleFactor: 1.0"

 

 

3) Is there a way to have the user control scaling via a GUI (i.e. similar to the actual Scale Feature pulling on 3 different axes) that just changes xScale, yScale, zScale ?

 

Screenshot 2023-01-10 at 10.56.20 PM.png

4) And if so, is there a way that updates the 3DMatrix Transform (e.g. in a triad)?  (If not, it's okay, I can create it directly).

 

Thank You,

Goutam Reddy

 

0 Likes
801 Views
5 Replies
Replies (5)
Message 2 of 6

kandennti
Mentor
Mentor

Hi @goutam_reddy .

 

It is possible that you do not understand the question, but I made a sample.

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None
_handlers = []

CMD_INFO = {
    'id': 'kantoku_test',
    'name': 'test',
    'tooltip': 'test'
}

_triadIpt: adsk.core.TriadCommandInput = None
_boolIpt: adsk.core.BoolValueCommandInput = None

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

    def notify(self, args: adsk.core.CommandCreatedEventArgs):
        adsk.core.Application.get().log(args.firingEvent.name)
        try:
            global _handlers
            cmd: adsk.core.Command = adsk.core.Command.cast(args.command)
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            onInputChanged = MyInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)


            global _triadIpt
            _triadIpt = inputs.addTriadCommandInput(
                '_triadIptId',
                adsk.core.Matrix3D.create()
            )
            _triadIpt.setScaleVisibility(False)

            global _boolIpt
            _boolIpt = inputs.addBoolValueInput(
                '_boolIptId',
                'move position',
                False,
                '',
                False
            )

        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class MyInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args: adsk.core.InputChangedEventArgs):
        global _boolIpt
        if not args.input == _boolIpt:
            return

        global _triadIpt
        mat: adsk.core.Matrix3D = _triadIpt.transform.copy()
        point, xVec, yVec, zVec = mat.getAsCoordinateSystem()
        point.translateBy(
            adsk.core.Vector3D.create(1,1,1)
        )
        mat.setWithCoordinateSystem(point, xVec, yVec, zVec)
        _triadIpt.transform = mat


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

    def notify(self, args: adsk.core.CommandEventArgs):
        global _triadIpt
        dump_matrix3D(_triadIpt.transform)


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

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.terminate()


def dump_matrix3D(mat: adsk.core.Matrix3D):
    pnt, xVec, yVec, zVec = mat.getAsCoordinateSystem()
    msglist = [
        '*****',
        f'origin:{pnt.asArray()}',
        f'x vecter:{xVec.asArray()}',
        f'y vecter:{yVec.asArray()}',
        f'z vecter:{zVec.asArray()}',
    ]

    global _app
    _app.log('\n'.join(msglist))


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

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            CMD_INFO['id']
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                CMD_INFO['id'],
                CMD_INFO['name'],
                CMD_INFO['tooltip']
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

Each time the "move position" button is clicked, the center of the TriadCommandInput moves 1cm.

1.png

0 Likes
Message 3 of 6

goutam_reddy
Participant
Participant

Hi,

 

Thanks for responding.  I've got "translation" and "rotation" working fine using the TriadCommandInput.

The problem is:  I can't get "scaling" to work properly.  There ought to be a way to just get a simple X,Y,Z scaling to show up (i.e. arrows), as suggested by the API.

Screenshot 2023-01-12 at 4.56.26 PM.png

However, I haven't found any particular combination of visibility settings that show just regular X,Y,Z scaling with arrows in the GUI.

Screenshot 2023-01-12 at 5.04.12 PM.png

Moreover, the scaling that does show up (planar scaling?) doesn't change the transformation array.... seems broken.

 

 

I'm just looking to replicate the Scale function (Design->Modify->Scale) from Fusion 360 as part of my add-in.

 

 

 

0 Likes
Message 4 of 6

kandennti
Mentor
Mentor

@goutam_reddy .

 

Sample corrected.

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None
_handlers = []

CMD_INFO = {
    'id': 'kantoku_test',
    'name': 'test',
    'tooltip': 'test'
}

_triadIpt: adsk.core.TriadCommandInput = None
_boolIpt: adsk.core.BoolValueCommandInput = None

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

    def notify(self, args: adsk.core.CommandCreatedEventArgs):
        try:
            global _handlers
            cmd: adsk.core.Command = adsk.core.Command.cast(args.command)
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            onInputChanged = MyInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)


            global _triadIpt
            _triadIpt = inputs.addTriadCommandInput(
                '_triadIptId',
                adsk.core.Matrix3D.create()
            )
            _triadIpt.setScaleVisibility(True)

            global _boolIpt
            _boolIpt = inputs.addBoolValueInput(
                '_boolIptId',
                'move position',
                False,
                '',
                False
            )

        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class MyInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args: adsk.core.InputChangedEventArgs):
        global _boolIpt
        if not args.input == _boolIpt:
            return

        global _triadIpt
        mat: adsk.core.Matrix3D = _triadIpt.transform.copy()
        point, xVec, yVec, zVec = mat.getAsCoordinateSystem()
        point.translateBy(
            adsk.core.Vector3D.create(1,1,1)
        )
        mat.setWithCoordinateSystem(point, xVec, yVec, zVec)
        _triadIpt.transform = mat


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

    def notify(self, args: adsk.core.CommandEventArgs):
        global _triadIpt
        dump_matrix3D(_triadIpt.transform)
        dump_TriadCommandInput_props(_triadIpt)


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

    def notify(self, args: adsk.core.CommandEventArgs):
        adsk.terminate()


def dump_TriadCommandInput_props(triadIpt: adsk.core.TriadCommandInput):
    props = [p for p in dir(triadIpt) if not '_' in p]
    infos = []
    for prop in props:
        try:
            infos.append(f'{prop} : {getattr(triadIpt, prop)}')
        except:
            pass

    global _app
    _app.log('\n'.join(infos))


def dump_matrix3D(mat: adsk.core.Matrix3D):
    pnt, xVec, yVec, zVec = mat.getAsCoordinateSystem()
    msglist = [
        '*****',
        f'origin:{pnt.asArray()}',
        f'x vecter:{xVec.asArray()} _ length:{xVec.length}',
        f'y vecter:{yVec.asArray()} _ length:{yVec.length}',
        f'z vecter:{zVec.asArray()} _ length:{zVec.length}',
    ]

    global _app
    _app.log('\n'.join(msglist))


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

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            CMD_INFO['id']
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                CMD_INFO['id'],
                CMD_INFO['name'],
                CMD_INFO['tooltip']
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

After changing "Scale X" and pressing OK, the "xScaleFactor" and "xScaleFactorExpression" properties output correctly.

1.png

 

However, if I continue to change "Scale X" and change other elements such as "Scale Y," "Scale X" returns to 1.00.

1.png

Perhaps these properties are just outputting the values from the dialog.

 

 

I think the closest thing to TriadCommandInput is the EDIT FORM dialog in the GUI.
Changing "Scale X" and manipulating "Scale Y" will do the same thing.
My guess is that TriadCommandInput's Scale may only be used to reflect changes immediately after they are made, like EDIT FORM.

1.png

(I pointed out that "X Angle" and "Y Angle" should be in "Numerical Inputs", but this has not been fixed.)

 

0 Likes
Message 5 of 6

goutam_reddy
Participant
Participant

Thanks for responding..

 

I decided to just program the scaling features manually, and not use a Triad for the scaling operation
(I couldn't get them to work....  i.e. just show the X,Y,Z scaling with arrows and updates to the transform (3DMatrix)).

Screenshot 2023-01-12 at 10.06.29 PM.png

 

As a future feature request, it would be great to be able to just implement scaling as done in the actual Fusion 360 program.  

 

I started looking at calling Fusion 360 Commands from the api as "text commands", but it looks like that methodology "has been deprecated"....  so not sure if it worth learning how to do it if it will be stripped out.

Anyway- thanks for looking at the problem and reproducing some of the same weird behaviors.

0 Likes
Message 6 of 6

ryan4
Community Visitor
Community Visitor

Can you explain how I can connect the triad to an existing sketch? So I can move the sketch around as needed? Thanks.

0 Likes