Accurate and repeatable setting of F360 camera viewpoint - help?

Accurate and repeatable setting of F360 camera viewpoint - help?

Anonymous
Not applicable
3,477 Views
9 Replies
Message 1 of 10

Accurate and repeatable setting of F360 camera viewpoint - help?

Anonymous
Not applicable

Hi folks -

 

I'm trying to use a Python script in F360 to set the camera view. Have some basic experience with Python but not with API programming. So would greatly appreciate some help.

About a year ago "nick" asked about a script to get and set the camera viewpoint:

https://forums.autodesk.com/t5/api-and-scripts/getting-setting-changing-the-camera-view/m-p/5520406#...

I have a similar need now and I tried the scripts that had been posted in that discussion. I want to define specific vews for documenation and reuse them for multiple drawings. Something like "30 deg right, 25 deg up, 0 deg roll". I ran into two problems:

 

1) The bug that was mentioned for "set camera" script - that the up-vector does not get set - is still here a year later. The up-axis of the model will not rotate to point up, A work around to get it point up is to first go to the home view and then run the script to set the camera view. I don't know if this can be done programatically.

2) Unfortunately F360 does not have any way to pick an exact view except for the predefined views. The only way to get other views (as far as I have found) is to drag with the mouse. This always results in some rotation of the up-vector (i.e. vertical model lines are off-vertical in the view) and no way to pick specific compass and elevation angles. In theory I could edit the information saved by the "save camera" script but I don't understand it. Three vectors are saved ("Eye", "target", and "upvector") but it is not clear what coordinate system they reference.

 

What I would really like is a dialog box where I could set the camera angle relative to the model coordinates. This would be similar to the vpoint command in Autocad. If the view could update with the dialog box open then with a little trial and error I could pick a nice view with angles at integer values instead of having many right-of-the-decimal-point digits. The values could be reused for other models. A graphical compass and elevation dialog like a modern version of Autocad would be nice but not really needed. Could the developers supply some equations to convert between the internal Eye/target/upvector vectors and more usable compass/elevation/roll vectors? Of course it would be nice to fix the upvector bug too.

 

I have suggested something like this in the IdeaStation area, but I didn't do a good job of convincing the community that it is important. So I would like to create a script to handle it. Oddly good old Autocad handles this really well while newer Solidworks has a marginal solution (using the arrow keys to rotate in fixed increments). I don't know Inventor but a quick Google search didn't turn up anything obvious.

 

Sorry for these ramblings. Any suggestions appreciated!

 

0 Likes
3,478 Views
9 Replies
Replies (9)
Message 2 of 10

ekinsb
Alumni
Alumni

Are you talking about controlling the orientation when you create a new view in a drawing?  If so, the API doesn't currently support any drawing functionality so it's not possible to automate any drawing workflows using scripts or add-ins.


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

Anonymous
Not applicable

Thanks for reply. No, I'm not trying to automate the drawing. This is about work in the model space and getting better control of the orbit behavior. I would like to be able to adjust the viewpoint in a precise way to best show features in a model and to show multiple models from the same viewpoint. I would also like to have the vertical axes in the model aligned vertically in the view. Then I can create a named view to use in a drawing or rendering.

 

This was an Autocad feature from way back when it first got 3D capabilities. I think that it has gotten lost in the more intuitive interfaces of modern 3D CAD software. Most of the time the mouse-centric Orbit tool is fine, but sometimes I do need more precision. Just for reference, here is how I do it in Solidworks: 1) start with an Ortho view, 2) use the left/right cursor keys to orbit horizontally in 5 degree (or whatever is set) steps, 3) use the up/down cursor keys to rotate orbit vertically, 4) name the new view, 5) (optional) make a note of how many left/right and up/down steps so I can repeat it in another model. Pretty kludgy and has limited resolution. It would be easier just to type in some view angles.

 

It is sort of funny for a  modern 3D CAD system to limit the precise views to ortho and isometric. These became standards because they were the easy to draw by hand. Why limit F360 (or any other CAD system) to ink and paper options? I can orbit to any view to best see features of a particular model. But I don't want to show a client a sloppy illustration where the verticals are askew or multiple components are seen from slightly different directions - see below.

 

Again - pardon the ramble!

 

view alignments

 

 

 

 

 

0 Likes
Message 4 of 10

ekinsb
Alumni
Alumni

The upVector problem that I referred to in my post was user-error and not actually a bug.  In the second function I use cam.up but the name of the property is "upVector".

 

Here's an updated version of that program that works correctly.  Here's the first function that captures the current camera and saves it to a specified file.

def saveView():
    import json
    
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface

        # Get the camera from the active viewport.
        cam = app.activeViewport.camera
        extentInfo = None
        if cam.cameraType == adsk.core.CameraTypes.PerspectiveWithOrthoFacesCameraType or cam.cameraType == adsk.core.CameraTypes.PerspectiveCameraType:
            isPerspective = True
            extentInfo = cam.perspectiveAngle
        else:
            isPerspective = False
            extentInfo = cam.viewExtents
            
        cameraInfo = {'eye':(str(cam.eye.x), str(cam.eye.y), str(cam.eye.z)), 'target':(str(cam.target.x), str(cam.target.y), str(cam.target.z)), 'up':(str(cam.upVector.x), str(cam.upVector.y), str(cam.upVector.z)), 'perspective':str(isPerspective), 'extent':str(extentInfo)}
                
        fileDlg = ui.createFileDialog()
        fileDlg.filter = 'Fusion Viewport Orientation (*.fvp)'        
        fileDlg.isMultiSelectEnabled = False
        fileDlg.title = 'Specify Fusion Viewport File'
        fileDlg.filterIndex = 0
        dialogResult = fileDlg.showSave()
        if dialogResult == adsk.core.DialogResults.DialogOK:
            viewportFilename = fileDlg.filename
        else:
            return

        myfile = open (viewportFilename, 'w')
        myfile.write(json.dumps(cameraInfo))
        myfile.close()               
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))    

 

And here's the second function that reads the specified file and applies that camera information to the active viewport.

def applyView():
    import json
    
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface

        fileDlg = ui.createFileDialog()
        fileDlg.filter = 'Fusion Viewport Orientation (*.fvp)'        
        fileDlg.isMultiSelectEnabled = False
        fileDlg.title = 'Specify Fusion Viewport File'
        fileDlg.filterIndex = 0
        dialogResult = fileDlg.showOpen()
        if dialogResult == adsk.core.DialogResults.DialogOK:
            viewportFilename = fileDlg.filename
        else:
            return

        myfile = open (viewportFilename, 'r')
        jsonData=myfile.read()        
        viewPortData = json.loads(jsonData)
        
        cam = app.activeViewport.camera
        
        eyeData = viewPortData['eye']
        eye = adsk.core.Point3D.create(float(eyeData[0]), float(eyeData[1]), float(eyeData[2]))
        
        targetData = viewPortData['target']
        target = adsk.core.Point3D.create(float(targetData[0]), float(targetData[1]), float(targetData[2]))
 
        upData = viewPortData['up']
        up = adsk.core.Vector3D.create(float(upData[0]), float(upData[1]), float(upData[2]))
        
        if viewPortData['perspective'] == 'False':
            isPerspective = False
        else:
            isPerspective = True
        extent = float(viewPortData['extent'])
        
        cam.eye = eye
        cam.target = target
        cam.upVector = up
        if isPerspective:
            cam.cameraType = adsk.core.CameraTypes.PerspectiveCameraType
        else:
            cam.cameraType = adsk.core.CameraTypes.OrthographicCameraType
            
        cam.viewExtents = extent
        
        app.activeViewport.camera = cam
    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 10

ekinsb
Alumni
Alumni

Writing a little program to modify the view by rotating a specific amount is also possible and is really just a variation of the second program above but instead of reading the camera information from a file you're computing new camera information.  For example, to rotate around a vertical axis will mean rotating the eye point around an axis that passes through the target point in the direction of the up vector.  A horizontal rotation will mean rotating the eye point around an axis that passes through the target point and in the direction of a vector that is the result of crossing the up vector with a target to eye vector.


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

Anonymous
Not applicable

Thanks for this information. I tried the two functions and confirmed that they work to save and restore the camera viewpoint.

I am still confused about the Up vector. It is a unit vector but I don't see how it is defined

I confirmed as follows, given coordinates target and eye:

target to eye vector:  R = (eye - target) = [eye[0]-target[0], eye[1]-target[1], eye[1]-target[1]

distance from the target to eye points:  D = length(R) =  (R[]0**2 + R[1]**2 + R[2]**2) ** 0.5

horizontal distance from target to point on ZX plane above or below camera: DH = ((R[]0**2 + R[2]**2) ** 0.5

horizontal or compass angle (referenced from +Z axis):  CompAng = acos( R[0] / DH )

The vertical or elevation angle  ElevAng = atan( R[1] / DH )

 

To this point the Up vector does not seem to matter, which seems at odds with the description in your message.  I would like to have a roll or twist angle. This should be the projected angle between the Y-axis and the camera's up direction as viewed along the vector R. Can you explain how the Up vector is defined so I can work that out?

 

Several other questions if you don't mind:

1) What is the meaning of the extants parameter?

2) Is there some example code for building simple dialog boxes (with editable text boxes) in F360?

3) Is it possible for the model window to update (to show a new camera position) while a dialog box remains open?

0 Likes
Message 7 of 10

Anonymous
Not applicable

OK - I tried to figure out how to create a dialog box in F360 and it looks like CommandInputs is what I should be using.

I found information in two places:

1. Learning / Programming Interface / Fusion 360 API Reference Manual / Objects / CommandInputs

      http://fusion360.autodesk.com/learning/learning.html?guid=GUID-504c1dbc-5132-454e-86fd-72101fa55d84

2. Creating Custom Fusion Commands

      http://help.autodesk.com/view/NINVFUS/ENU/?guid=GUID-3922697A-7BF1-4799-9A5B-C8539DF57051

 

Both have Python example scripts. The first ran but gave several error messages - I think all related to 'resources'. The 2nd would not run at all. First there was a indentation error which I fixed and then other errors I could not figure out. The examples show creation of the dialog elements but nothing about setting or getting the contents. I am not sure where to look for that in the Learning or Help pages.

 

Even if it worked, the Python code is daunting. I use Python fairly regularly and have done a little GUI programming with it, but here I can barely figure out the structure, much less the details. If I had a few weeks to spend I could probably work it out. What I am trying to do is pretty simple in concept - display a few numbers, let the user modify them and press an update button, and send the new values along to be processed. The background work is mostly just the code samples you posted plus some trigonometry equations.

 

It is unfortunate that there is not a friendlier entry into scripts. Perhaps you folks are so familiar with the interface that you don't realize how hard it is to get started. I remember when I first learned some GUI programming there was an example with a simple dialog - two text boxes and a button. When you edited the first text box the second updated to match it immediately. When you edited the second text box the first did not update till you clicked the button. So there in a very short script were text box and button creation, setting text box content, and two ways of getting the text box content. Once I understood that little script I was ready to learn more. Much more useful to a novice than showing a dozen ways of creating an input element (some of which don't work) but nothing about how to actually use them.

 

So where to go? Either I need someone else to write the CommandInputs code or maybe I can make this work on the command line. Still need to understand how the Up vector is defined. Any suggestions appreciated!

0 Likes
Message 8 of 10

ekinsb
Alumni
Alumni

All of what you want to do is possible.  The creation of a command GUI isn't as simple as we would like either.  Unfortunately there isn't any language I'm aware of that works on both Windows and Mac that provides a nice visual GUI builder like Visual Basic or C#.  All of them require coding.  Having said that, I think there are some things we can do in the future to make writing a Fusion command easier.  Certainly there can be some improvements in the documentation and possibly in the API itself.

 

Here's a blog post that was written for Inventor but the concepts also apply to Fusion cameras that hopefully will help with the camera concepts.

http://modthemachine.typepad.com/my_weblog/2013/09/working-with-cameras-part-1.html


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

Anonymous
Not applicable

Hi Brian,

 

is your code working with the latest version Fusion360? I have been tryig to run it without success, but I wonder if problem is the outdated code or just my lack of scripting knowledge 😞

 

Precise camera location is critical for our new project (4m from the origin, camera height 1,75m and focal length of 45mm to mimic human vision).

 

Cheers from Holland,

Dejan

0 Likes
Message 10 of 10

ekinsb
Alumni
Alumni

Here's a very simple little script that sets the camera based on two selected points where they are used as the target and eye points of the camera.  I've hard-coded the up direction and the angle.  I'm not sure what perspective angle equates to a 45 mm focal length so you can play with that too.

 

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

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        view = app.activeViewport
        if view.camera.cameraType != adsk.core.CameraTypes.PerspectiveCameraType:
            ui.messageBox('The view must be a perspective view.')
            return
 
        # Assumes the up direction is Z.       
        upVector = adsk.core.Vector3D.create(0,0,1)

        targetSel = ui.selectEntity('Select the target point', 'SketchPoints, ConstructionPoints, Vertices')
        targetPnt = adsk.core.Point3D.cast(None)
        if targetSel:
            if type(targetSel.entity) is adsk.fusion.SketchPoint:
                skPnt = adsk.fusion.SketchPoint.cast(targetSel.entity)
                targetPnt = skPnt.worldGeometry
            else:
                targetPnt = targetSel.entity.geometry
                
        
        eyeSel = ui.selectEntity('Select the eye point', 'SketchPoints, ConstructionPoints, Vertices')
        eyePnt = adsk.core.Point3D.cast(None)
        if eyeSel:
            if type(eyeSel.entity) is adsk.fusion.SketchPoint:
                skPnt = adsk.fusion.SketchPoint.cast(eyeSel.entity)
                eyePnt = skPnt.worldGeometry
            else:
                eyePnt = eyeSel.entity.geometry
        
        cam = view.camera        
        cam.eye = eyePnt
        cam.target = targetPnt
        cam.upVector = upVector
        cam.perspectiveAngle = 55 * (math.pi/180)

        view.camera = cam
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

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