Kean Walmsley


  • About the Author
    Kean on Google+

July 2014

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    








« A couple of streamlined solutions for AutoCAD using LINQ | Main | Technology advances & the Managed Windows API »

January 19, 2011

Returning an average point to the AutoCAD command-line using .NET

This request came in over the weekend:

There is a useful object snap in AutoCAD for the mid point of 2 selected points. I would like a midpoint (average) of 3 (or more) points. Could this work in 3D as well as 2D?  It’s useful when drawing building surveys, often you triangulate a point from several and there are often ‘minimal’ differences in the dimension and you just take the average.

Given the fact we’re actually talking about an arbitrary number of points that will almost certainly not belong to a single entity, object snaps are probably neither the easiest nor the most appropriate way to solve this problem.

The below C# code registers a transparent command – AVG – which can be called from within drafting or modelling commands to calculate the average of a set of points (which can be selected using object snapping, should the user so wish). The resulting point is sent to the command-line – so the command really does need to be called transparently to be useful – which obviates the need for an object snap to be implemented.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

using System.Linq;

using System;

 

namespace AveragePoint

{

  public class Commands

  {

    [CommandMethod("AVG", CommandFlags.Transparent)]

    public void AveragePoint()

    {

      Document doc =

          Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      // Prompt will be reused in our loop

 

      PromptPointOptions ppo =

        new PromptPointOptions("\nSelect point to average: ");

      ppo.AllowNone = true;

 

      // List of the points selected and our result object

 

      List<Point3d> pts = new List<Point3d>();

      PromptPointResult ppr;

 

      // The selection loop

 

      do

      {

        // If we have a valid point selection, add it to the list

 

        ppr = ed.GetPoint(ppo);

        if (ppr.Status == PromptStatus.OK)

          pts.Add(ppr.Value);

      }

      while (ppr.Status == PromptStatus.OK);

 

      // The loop will continue until a non-OK result...

      // We only care if Enter was used to terminate

 

      if (ppr.Status == PromptStatus.None)

      {

        // Create a string containing the point information:

        // the average of the selected coordinates

 

        string cmd =

          String.Format(

            "{0},{1},{2} ",

            pts.Average(a => a.X),

            pts.Average(a => a.Y),

            pts.Average(a => a.Z)

          );

 

        // Send it to the command-line

 

        doc.SendStringToExecute(cmd, true, false, true);

      }

    }

  }

}

Part of the beauty of this command (if I do say so myself ;-) is in its use of LINQ to average the list of points. It calculates the average X, Y and Z coordinates using a LINQ extension method on the List<> class, placing the results in a string that gets squirted into the command-line. We could just as easily have created a Point3d from these averages, of course, had that been the requirement.

Here’s how you’d use the command. Let’s start with some basic geometry:

Some basic 2D geometry

Let’s now launch the LINE command, and then call AVG transparently. We’ll do so one with the end-points of the line, once with the corners of the triangle and finally with those of the rectangle. We can see we end up with  two lines connecting the mid-points of these shapes.

Shapes connected by their mid-points

Here’s the command-line output, to see it at work:

Command: LINE

Specify first point: 'AVG

>>Select point to average:

>>Select point to average:

>>Select point to average:

Resuming LINE command.

Specify first point: 8.00080539552933,15.9545432099352,0

Specify next point or [Undo]: 'AVG

>>Select point to average:

>>Select point to average:

>>Select point to average:

>>Select point to average:

Resuming LINE command.

Specify next point or [Undo]: 15.9604077207532,14.8917902344987,0

Specify next point or [Undo]: 'AVG

>>Select point to average:

>>Select point to average:

>>Select point to average:

>>Select point to average:

>>Select point to average:

Resuming LINE command.

Specify next point or [Undo]: 26.731968038993,16.1501419209108,0

Specify next point or [Close/Undo]:

Command:

Here’s a quick test to see it working in 3D: the AVG was used to calculate the average of the peaks of three cones, returning the point for use as the start-point of a line:

The average point of the tops of three cones

As well as working in both 2D and 3D, the command appears to deal with custom user-coordinate systems without any trouble (as the average is made of the selected point – which is provided in UCS coordinates – before being sent to the command-line, which also expects UCS coordinates).

One outstanding concern I have – as it sends a point as a string to the command-line – is how it works in locales which use commas as the decimal point. I did some cursory testing, but if someone else could give it a spin and let me know how it works, that’d be great.

Although a very simple piece of code, this may well become a future Plugin of the Month: its simplicity actually makes it ideal as a project demonstrating the benefits of custom development.

blog comments powered by Disqus

10 Random Posts