NavisWorks C# Pick Point

Lest deep dive in Autodesk NavisWorks api.

First, we need a tool plugin with mouse down Interceptor:

using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;

    [Plugin("PickModelPointPlugin", "ADSK")]
    class PickModelPointPlugin : ToolPlugin
    {
        public override bool MouseDown(View view, KeyModifiers modifiers,
                                         ushort button, int x, int y,
                                         double timeOffset)
        {
                // get current selection
                PickItemResult pickedResult = view.PickItemFromPoint(x, y);

                if (pickedResult != null)
                {
                    Autodesk.Navisworks.Api.Point3D clickedPoint =          
                                                        pickedResult.Point;

                // Sent to static exchange, to make click result available 
                // for Addin Plugins in namespace
                // convert to custom Point, to reduce coupling. 
                StaticExchange.PickedPoint = new cPoint(clickedPoint.X,
                                                        clickedPoint.Y,
                                                        clickedPoint.Z);
                }
            return base.MouseDown(view, modifiers, button, x, y, 
                                  timeOffset);
        }
    }

First, we need a tool plugin with mouse down Interceptor:

   class StaticExchange
    {
        public static event PointDelegate PointUpdate;
        public delegate void PointDelegate(PointEventArgs e);
        private static cPoint point;

        public static cPoint PickedPoint
        {
            get {
                    return point;
                }
            set {
                    point = value;
                    PointUpdate?.Invoke(new PointEventArgs(point));
                }
        }
    }

Obviously, we may need to do some actions as soon as user click on something. That`s why event delegate has been presented in StaticExchange.
Finally, in our Addin we call A plugin and await for result. In my case it’s a form with buttons, so on a button I bind click handler:

private async void btnPickNavisPoint_Click(object sender, EventArgs e)
{
      try { 
            //search for plugin availability 
            ToolPluginRecord toolPluginRecord =                    
                   (ToolPluginRecord)ANA.Application.Plugins
                       .FindPlugin("PickModelPointPlugin.ADSK");
        //set Plugin for execution                                
        ANA.Application.MainDocument.Tool
            .SetCustomToolPlugin(toolPluginRecord.LoadPlugin());
                //Subscribe on event
                StaticExchange.PointUpdate += onPointUpdate; 
                //Patiently wait for result
                Point3D result = await WaitForUserChoiceAsync();

                MessageBox.Show(
                    "Point: "+
                     "X:" + result.X.ToString("0.##") + " " +
                     "Y:" + result.Y.ToString("0.##") + " " +
                     "Z:" + result.Z.ToString("0.##") + " ");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                //Unsubscribe and switch back to Tool.Select
                StaticExchange.PointUpdate -= onPointUpdate; 
                ANA.Application.MainDocument.Tool.Value = Tool.Select;
            }
        }

And now final peace – where magic is live.

  1. Declare TaskCompletionSource,
  2. Wrap it into WaitUserChoice.
  3. In event handler call Source and pass argument.
        private TaskCompletionSource<Point3D> _userChoiceTcs;

        private async Task<Point3D> WaitForUserChoiceAsync()
        {
            // Create a new TaskCompletionSource for this operation
            _userChoiceTcs = new TaskCompletionSource<Point3D>();
            try
            {
                Point3D result = await _userChoiceTcs.Task;
                return result;
            }
            finally
            {
                // Clean up
                _userChoiceTcs = null;
            }
        }

        private void onPointUpdate(PointEventArgs p)
        {
            //Here come the user click result
            _userChoiceTcs?.TrySetResult(new Point3D(p.Point.X,
                p.Point.Y,
                p.Point.Z));
        }

As a result we got message with coordinates of clicked points, as soon as user click on some object.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *