Community
Navisworks API
Welcome to Autodesk’s Navisworks API Forums. Share your knowledge, ask questions, and explore popular Navisworks API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

trigger view cube "reframe" when selection change

8 REPLIES 8
SOLVED
Reply
Message 1 of 9
ngombault
1852 Views, 8 Replies

trigger view cube "reframe" when selection change

Hi,

 

When clicking on the faces, edges or corners of the view cube, navisworks window reframes to fit selected object on the screen.

I am trying to trigger that behavior using the API when the selection changes.

I couldn't find any API reference that seemed able to call the view cube functions directly, besides I'd like to let the user specify the reframing ratio (by default the view cube fit the selected objects to the entire window which often makes the context hard to see, 1/2 or 1/3 window would be better). I did find the TopLeftFront function of the View object but it seem to only reset the scene.

 

So I started writting my own function, using the viewpoint object and the modelitemcollection.boundingbox function to get the selected objects dimension.

After some intensive trigonometric geomery, I am now able to replicate the "edge click" and "face click" functions of the cube, with nice parameter for fit ratio and view angle.

The "corner click" functions however has prooven to be a challenge of another level entirely.

 

I welcome all suggestions on how to create a "TopLeftFront" view of selected objects that would fit the screen on a given ratio (vertical or horizontal, whichever is the largest).

 

attached is an example of the view cube fiting, the ratio being always 1.

 

cheers,

Nic

 

8 REPLIES 8
Message 2 of 9
xiaodong_liang
in reply to: ngombault

Hi,

 

probably there is other solutions, this is what I got. Hope it helps.

 

 

            private void TopLeft()
             {
                 Document oDoc =
                     Autodesk.Navisworks.Api.Application.ActiveDocument;
 
                 //current selection
                 ModelItemCollection oMC = oDoc.CurrentSelection.SelectedItems;
 
                 //BundingBox of the selection
                 BoundingBox3D oBox = oMC.BoundingBox();
 
                 // make a copy of current viewpoint
                 Viewpoint oCurrVCopy = oDoc.CurrentViewpoint.CreateCopy();
 
                 double customRatio = 1.2;
                 //set new focal to zoom to the selection
                 double oNewFocal = oBox.Max.DistanceTo(oBox.Min) * customRatio;
 
                 oCurrVCopy.FocalDistance = oNewFocal;                 
                
 
                 // new target is the center of the selection
                 Point3D oNewTarget = oBox.Center;
 
                 //new direction is (TOP LEFT)
                 Vector3D oNewViewDir = new Vector3D(1, 1, -1);
               
                 //calculate the new position by the target and focal distance
                 Point3D oNewPos = new Point3D(oNewTarget.X - oNewViewDir.X * oNewFocal,
                     oNewTarget.Y - oNewViewDir.Y * oNewFocal,
                     oNewTarget.Z - oNewViewDir.Z * oNewFocal);
 
                 //set the position
                 oCurrVCopy.Position = oNewPos;
                 //set the target
                 oCurrVCopy.PointAt(oNewTarget);
                 //set view direction
                 oCurrVCopy.AlignDirection(oNewViewDir);
                 // set which direction is up: in this case it is Z+
                 oCurrVCopy.AlignUp(new Vector3D(0, 0, 1));
 
                 // update current viewpoint
                 oDoc.CurrentViewpoint.CopyFrom(oCurrVCopy);
             }
Message 3 of 9
ngombault
in reply to: xiaodong_liang

Hi Xiaodong,

 

Thank you for the suggestion.

When I tried it though, it the results were a bit approximative, both in terms of centering the object on the screen and for the size ratio, not reacting well to variation of the FOV.

 

I know I won't cover extreme cases with FOV close to 180deg for instance, but there are some consideration I think I cannot avoid even in the range 20-160, for instance the viewpoint target is only the bounding box center when the FOV is null (orthographic view); in perspective mode it moves toward the edges depending on the FOV.

 

I have major OCD, and so I am in search of the exact solution.

 

You used Boundingbox.Max and Min, can you please tell me which points these refer too exactly?

 

Thanks,

Nic

Message 4 of 9
xiaodong_liang
in reply to: ngombault

Hi,

the boundingbox3d is axis aligned 3D bounding box. So the min point is the left-bottom corner and the max is the top right corner in right hand coordinate system.

As to the exact solution, I will need some time to dig into.
Message 5 of 9
ngombault
in reply to: xiaodong_liang

Thanks.
I am only working on this sporadically but I am almost done on my side, I'll post the code once it is complete. I was just hoping I wouldn't have to calculate nasty rotation matrices...

Any chance the API could expose the viewcube-like-reframing in the future?

Nic
Message 6 of 9
xiaodong_liang
in reply to: ngombault

Hi,

I thought my codes in message 2 has solved your original question. I was struggling to understand your further comment in the message 3. So I asked our expert to take a look. He is also not entirely sure what you want.

 

He said it should be pretty easy to replicate ViewCube style code. You can already determine the orientation of the model using the Front and Up vectors. From those two vectors, you can compute all size directions (Front, Back, Left, Right, Top, Bottom). Then, when the user changes the selection, you can create a camera that is centered on the selection, with the appropriate view direction, then zoom until the bounding box is in view. This is exactly what my code does.

 

So, could you elaborate: if my codes solves your orignal question and what your further questions exactly is? 

 

As to the  viewcube-like-reframing, we have had the wish to expose ViewCube in API, but this is just a wish at this moment. I cannot guarantee when it would be implemented.

 

 

Message 7 of 9
ngombault
in reply to: xiaodong_liang

Hi,

Sorry if it wasn't clear enough.

 

basically I am trying to fit the bounding box of the selection on the screen so that:

- the dimensions of its projection on the screen divided by the screen dimensions are inferior or equal to a given ratio (Hb/Hs <= R AND Vb/Vs <=R), and

- the bounding box projection is centered both horizontally and vertically on the screen.

 

TopFrontRight_Diagram.JPG  goal.JPG

 

 

when looking at the bounding box from the FrontTopRight corner, the longest dimension of the bounding box projection horizontally is generally given by the projection of the vector AC, but it can be AB or BC with very high FOV. Vertically it is generally the vector BiD but it can be BiB with high FOV (try it out in Navis with the vew cube, a dummy rectangular box (see attached nwd) and a high FOV like 120-140).

 

These conditions will give you a minimum distance to be away from the object AND the position both vertically and horizontally, which does not conincide with the center of the box (again try the view cube in Navis and see where the pivot point of the rotation is once the object is reframed, it is not the center of the box).

 

Since we know the coordinates of all points in the 3D space (X,Y,Z) we can calculate the coordinates of these points in the screen axis system, by using 2 rotation matrices, one around Z (angle teta) and one around X (angle phi). It gets a little tricky after the first rotation, since the rotation axis of the second rotation is X (1,0,0) BUT expressed in the rotated axis system (cos Teta, Sin Teta, 0), and that gives a nasty second rotation matrix. Wikipedia being allknowing, it also knows the coefficient of the second matrix.

 

I am sure having a lot of fun doing all this, but a more straigh forward solution would be appreciated. So I definitly vote in favor of the view cube API.

 

Regards,

Nic

 

 

Message 8 of 9
ngombault
in reply to: ngombault

PS: the axis system drawn above is for TopLeftFront not TopFrontRight
Message 9 of 9
ngombault
in reply to: ngombault

So after a few hours of headache on axis systems and rotations (the axis I mention above are wrong), here is what I got:

 

The main sub is ReframeOnObjects3D() it calls the Rotate() function to switch between axis systems.

You can play with the ratio and 2 angles parameters to get edges, corners or face views. Note that the example is limited to 0 <= teta, phi <= Pi/2 and so only covers Front, Top and Right faces, but you can adapt it for the 3 other faces if needed.

 

The sub Reframes() demonstrate some standard views, I used a 0.99 ratio for debuging to make sure the box was not slighly cropped, but a ratio of 2/3 usually gives a good view.

 

Make sure objects are selected in 3D before you run the code.

 

 

 

    Public Sub ReframeOnObjects3D(FitRatio As Double, teta As Double, phi As Double)

        'Remarks about the setup:
        '1) Navisworks axis system (X,Y,Z) is right handed with the Front view in the plane (YZ), Z Up and X going toward the screen
        '2) Corners of the superior face of the bounding box are named A, B, C and D
        '   - D is the origin (0,0,0)
        '   - DA aligned with X, DC aligned with Y
        '   Corners of the inferior face are named Ai, Bi, Ci and Di, after the corresponding top corner
        '3) The axis system of the screen (x,y,z) has x horizontally, z vertically and y toward the object
        '4) Rteta is the rotation around Z and Rphi the rotation around X so that Rteta * Rphi * (X,Y,Z) = (x,y,z)

        'This gives
        '1) Projection p of a point P on the screen:
        '       pi = Pi*Fd/Py for all P(x,y,z); iE(x,z); Fd = Focal distance
        '2) Projection lengths centered:
        '       |ax| = |cx| horizontally with a and c extremum points
        '       |bix| = |dx| vertically with bi and d extremum points
        '3) Projection inferior to given ratio of screen length:
        '       cx-ax <= RHe; He = Horizontal extent at focal distance
        '       dz-bix <= RVe; Ve = Vertical extent at focal distance



        Dim debug As New System.Text.StringBuilder

        Dim Items As ModelItemCollection = Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems

        'Get the items bounding box dimensions
        Dim boundingBox As BoundingBox3D = Items.BoundingBox(False)
        Dim G As Point3D = boundingBox.Center
        Dim W As Double = boundingBox.Size.X
        Dim L As Double = boundingBox.Size.Y
        Dim H As Double = boundingBox.Size.Z

        'Set the rotation angles around z and x
        'Warning: vector x for second rotation must be expressed
        'in the axis system already rotated around z (inverse rotation)
        Dim Rteta As Double = -Math.PI / 2 - teta
        Dim URteta As New UnitVector3D(0, 0, 1) 'around z
        Dim Rphi As Double = phi
        Dim URphi As New UnitVector3D(Rotate(New UnitVector3D(1, 0, 0), -Rteta, URteta)) 'around x (in Rz axis system)

        debug.Append("URphi:")
        debug.AppendLine(URphi.ToString)

        'Create a modifiable copy of the current viewpoint
        Dim nvp As Viewpoint = Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentViewpoint.Value.CreateCopy()


        'Get the minimum distance By to respect the ratio horizontally
        'the dimensionning vectors will be AC with small FOV*FitRatio, AB or BC with high FOV*FitRatio
        Dim Th As Double = FitRatio * nvp.HorizontalExtentAtFocalDistance / (2 * nvp.FocalDistance)

        Dim rAC As Vector3D = Rotate(Rotate(New Vector3D(-W, L, 0), Rphi, URphi), Rteta, URteta)
        Dim rAB As Vector3D = Rotate(Rotate(New Vector3D(0, L, 0), Rphi, URphi), Rteta, URteta)
        Dim rBC As Vector3D = Rotate(Rotate(New Vector3D(-W, 0, 0), Rphi, URphi), Rteta, URteta)

        debug.AppendLine()
        debug.AppendLine("Vectors:")
        debug.AppendLine("AC: " & rAC.ToString)
        debug.AppendLine("AB: " & rAB.ToString)
        debug.AppendLine("BC: " & rBC.ToString)

        Dim MinByAC As Double = (rAC.X / Th - rAC.Y) / 2 + rAB.Y
        Dim MinByAB As Double = (rAB.X / Th + rAB.Y) / 2
        Dim MinByBC As Double = (rBC.X / Th - rBC.Y) / 2

        Dim MinByH As Double = Math.Max(Math.Max(MinByAC, MinByAB), MinByBC)


        'Get the minimum distance By to respect the ratio vertically
        'the dimensionning vectors will be DBi with small FOV*FitRatio, BBi with high FOV*FitRatio
        Dim Tv As Double = FitRatio * nvp.VerticalExtentAtFocalDistance / (2 * nvp.FocalDistance)

        Dim rBiD As Vector3D = Rotate(Rotate(New Vector3D(-W, -L, H), Rphi, URphi), Rteta, URteta)
        Dim rBiB As Vector3D = Rotate(Rotate(New Vector3D(0, 0, H), Rphi, URphi), Rteta, URteta)

        debug.AppendLine("BiD: " & rBiD.ToString)
        debug.AppendLine("BiB: " & rBiB.ToString)

        Dim MinByBiD As Double = (rBiD.Z / Tv - rBiD.Y) / 2 + rBiB.Y
        Dim MinByBiB As Double = (rBiB.Z / Tv + rBiB.Y) / 2

        Dim MinByV As Double = Math.Max(MinByBiD, MinByBiB)


        debug.AppendLine()
        debug.AppendLine("Dimensionning:")

        'Get the overall minimum distance
        Dim Bx, By, Bz As Double
        If MinByH >= MinByV Then

            'the horizontal is dimensionning
            By = MinByH

            'Get Bx depending on the longest H vector
            Select Case MinByH

                Case MinByAC
                    Bx = (Th * rAC.Y - rAC.X) / 2 + rAB.X
                    debug.Append("H-AC")

                Case MinByAB
                    Bx = (Th * rAB.Y + rAB.X) / 2
                    debug.Append("H-AB")

                Case MinByBC
                    Bx = (Th * rBC.Y - rBC.X) / 2
                    debug.Append("H-BC")

            End Select

            'since the horizontal is dimensionning, the vertical ratio
            'is inferior to the target ratio so we calculate the actual
            'ratio on the vertical and the resulting Bz
            Dim VFitRatio As Double = 0.0
            If MinByBiD >= MinByBiB Then

                'VFitRatio = (rBiD.Z * nvp.FocalDistance) / ((By - rBiB.Y + rBiD.Y / 2) * nvp.VerticalExtentAtFocalDistance)
                'Tv = VFitRatio * nvp.VerticalExtentAtFocalDistance / (2 * nvp.FocalDistance)
                Tv = rBiD.Z / ((By - rBiB.Y + rBiD.Y / 2) * 2)
                Bz = (Tv * rBiD.Y - rBiD.Z) / 2 + rBiB.Z
                debug.AppendLine("; V-BiD")

            Else

                Tv = rBiB.Z / ((By - rBiB.Y / 2) * 2)
                Bz = (Tv * rBiB.Y + rBiB.Z) / 2
                debug.AppendLine("; V-BiB")

            End If


        Else

            'the vertical is dimensionning
            By = MinByV

            'Get Bz depending on the longest V vector
            If MinByBiD >= MinByBiB Then

                Bz = (Tv * rBiD.Y - rBiD.Z) / 2 + rBiB.Z
                debug.Append("V-BiD")

            Else

                Bz = (Tv * rBiB.Y + rBiB.Z) / 2
                debug.Append("V-BiB")

            End If

            'since the vertical is dimensionning, the horizontal ratio
            'is inferior to the target ratio so we calculate the actual
            'ratio on the horizontal and the resulting Bx
            Dim HFitRatio As Double = 0.0
            Select Case MinByH

                Case MinByAC
                    'HFitRatio = (rAC.X * nvp.FocalDistance) / ((By - rAB.Y + rAC.Y / 2) * nvp.HorizontalExtentAtFocalDistance)
                    'Th = HFitRatio * nvp.HorizontalExtentAtFocalDistance / (2 * nvp.FocalDistance)
                    Th = rAC.X / ((By - rAB.Y + rAC.Y / 2) * 2)
                    Bx = (Th * rAC.Y - rAC.X) / 2 + rAB.X
                    debug.AppendLine("; H-AC")

                Case MinByAB
                    Th = rAB.X / ((By - rAB.Y / 2) * 2)
                    Bx = (Th * rAB.Y + rAB.X) / 2
                    debug.AppendLine("; H-AB")

                Case MinByBC
                    Th = rBC.X / ((By + rBC.Y / 2) * 2)
                    Bx = (Th * rBC.Y - rBC.X) / 2
                    debug.AppendLine("; H-BC")

            End Select


        End If



        'Apply inverse rotation to BO(x,y,z) to get BO(X,Y,Z) and
        'where O is the camera position in 3D
        Dim BO As Vector3D = Rotate(Rotate(New Vector3D(-Bx, -By, -Bz), -Rteta, URteta), -Rphi, URphi)
        Dim GB As New Vector3D(W / 2, L / 2, H / 2)
        nvp.Position = G.Add(GB).Add(BO)

        'By definition the view alignment is perpendicular to the screen y=(0,1,0)
        'we applied the inverse rotation to get y=(X,Y,Z) coordinates
        Dim ViewDirection As Vector3D = Rotate(Rotate(New UnitVector3D(0, 1, 0), -Rteta, URteta), -Rphi, URphi)
        nvp.AlignDirection(ViewDirection)

        'AlignUp aligned to Z for (any teta ; phi <= PI/4)
        nvp.AlignUp(New UnitVector3D(0, 0, 1))

        debug.AppendLine()
        debug.AppendLine("View Alignment:")
        debug.AppendLine(ViewDirection.ToString)

        'Replace current viewpoint
        Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentViewpoint.CopyFrom(nvp)

        MessageBox.Show(debug.ToString)


    End Sub

 

    Private Function Rotate(v As Vector3D, angle As Double, u As UnitVector3D) As Vector3D

        Dim C As Double = Math.Cos(angle)
        Dim S As Double = Math.Sin(angle)

        Return New Vector3D((C + u.X ^ 2 * (1 - C)) * v.X + (u.X * u.Y * (1 - C) - u.Z * S) * v.Y + (u.X * u.Z * (1 - C) + u.Y * S) * v.Z,
                            (u.Y * u.X * (1 - C) + u.Z * S) * v.X + (C + u.Y ^ 2 * (1 - C)) * v.Y + (u.Y * u.Z * (1 - C) - u.X * S) * v.Z,
                            (u.Z * u.X * (1 - C) - u.Y * S) * v.X + (u.Z * u.Y * (1 - C) + u.X * S) * v.Y + (C + u.Z ^ 2 * (1 - C)) * v.Z)

    End Function

 

 

 

 

 

 

    Private Sub Reframes()

        ReframeOnObjects3D(0.99, 0, 0) 'Front
        ReframeOnObjects3D(0.99, Math.PI / 2, 0) 'Right
        ReframeOnObjects3D(0.99, 0, Math.PI / 2) 'Top

        ReframeOnObjects3D(0.99, Math.PI / 4, 0) 'FrontRight
        ReframeOnObjects3D(0.99, 0, Math.PI / 4) 'FrontTop
        ReframeOnObjects3D(0.99, Math.PI / 2, Math.PI / 4) 'RightTop

        ReframeOnObjects3D(0.99, Math.PI / 4, Math.PI / 4) 'TopFrontRight / Awesome

        ReframeOnObjects3D(0.99, Math.PI / (1 + Math.Sqrt(5)), Math.PI / 8.12) 'Useless / Architect

    End Sub

 

 

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

Post to forums  

Rail Community


Autodesk Design & Make Report