AutoCAD 2016: Extracting floorplans from point clouds using .NET

A little while ago you may remember an HTML progress meter I created while looking at "future API features". The API feature in question was of course for AutoCAD 2016, and related to the extraction of floorplans programmatically using .NET, a topic we're covering in today's post.

We're going to see some fairly basic code that asks AutoCAD to analyse a point cloud – that we're going to attach from an RCS or RCP file – and generate polyline boundaries for its floorplan.

Now I didn't actually have a great point cloud to test this, so I ended up using one I'd captured when testing the Kinect v2 sensor:

Kinect point cloud

[On a related note, I've been working with a FARO Freestyle3D scanner for the last few weeks: once I publish more on using that I'll hopefully revisit this code to see how it performs on a more real-world dataset.]

The .NET API has a more rudimentary set of extraction features than the C++ API, during this release: in ObjectARX you have the AcPointCloudExtractedCylinder class, for instance, which presumably means you can use the extraction feature also to extract cylinders rather than just polylines.

Here's some C# code implementing the EFP command (for ExtractFloorPlan) that makes use of the code in this previous post as well as the HTML progress meter file you can find here:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Colors;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System;

 

namespace PointCloudAnalysis

{

  public class Commands

  {

    [CommandMethod("EFP")]

    public void ExtractFloorPlan()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;

 

      using (var tr = db.TransactionManager.StartTransaction())

      {

        var msId = SymbolUtilityServices.GetBlockModelSpaceId(db);

 

        // First let's attach the point cloud from the RCS (or RCP)

 

        var pcId =

          PointCloudEx.AttachPointCloud(

            "c:\\temp\\pc.rcs",

            new Point3d (0,0,1),

            1,

            0,

            db

          );

 

        // And get it as a PointCloudEx object

 

        var pc =

          tr.GetObject(pcId, OpenMode.ForRead) as PointCloudEx;

 

        // Might also want to adjust MiniumSegmentLength and FillGap

 

        var eo = new ExtractOption();

        eo.ExtractType = ExtractionType.AllLine;

 

        // Attempt an extraction

 

        try

        {

          var res =

            PointCloudExtractor.Extract(

              pc,

              Vector3d.ZAxis,

              Vector3d.XAxis,

              Point3d.Origin,

              eo,

              new DisplayPointCloudExtractionProgress()

            );

 

          // If we have results...

 

          if (res != null)

          {

            // Add the various polyline profiles to the modelspace

            // (and make them red)

 

            var col = Color.FromColorIndex(ColorMethod.ByAci, 1);

            var ids =

              PointCloudExtractor.AppendPolylineProfile(

                res, msId, "0", col, 0.0

              );

          }

        }

        catch

        {

          pc.UpgradeOpen();

          pc.Erase();

        }

 

        tr.Commit();

      }

    }

  }

 

  public class DisplayPointCloudExtractionProgress

    : IPointCloudExtractionProgressCallback

  {

    private ProgressMeterHtml _pm;

    private int _ticks;

    private bool _started;

 

    public DisplayPointCloudExtractionProgress()

    {

      _pm = new ProgressMeterHtml();

      _pm.SetLimit(100);

 

      _ticks = 0;

      _started = false;

    }

 

    public void End()

    {

      _pm.Stop();

    }

 

    public void Cancel()

    {

      _pm.Cancel();

    }

 

    public bool Cancelled()

    {

      if (_pm.Cancelled)

      {

        _pm.AdditionalInfo(" ");

        _pm.Stop();

        return true;

      }

      return false;

    }

 

    public void UpdateRemainTime(double t)

    {

      if (t > 0)

      {

        _pm.AdditionalInfo(

          Math.Round(t, 2).ToString() + " seconds remaining."

        );

      }

    }

 

    public void UpdateCaption(string s)

    {

      if (_started)

      {

        _pm.Caption(s);

      }

      else

      {

        _pm.Start(s);

        _started = true;

      }

    }

 

    public void UpdateProgress(int i)

    {

      while (_ticks < i)

      {

        _pm.MeterProgress();

        System.Windows.Forms.Application.DoEvents();

        _ticks++;

      }

 

      System.Windows.Forms.Application.DoEvents();

 

      if (i == 100)

      {

        End();

      }

    }

  }

}

Here's how the EFP command works when run against the above-mentioned point cloud.

ExtractFloorPlan

Now let's take a closer look at the results. You can at least see we have some manner of boundary extracted: if we tweak the extraction options we can presumably increase the accuracy (something we'll try to look at when we have a properly-captured point cloud of an office space to work with).

Point cloud analysis

3 responses to “AutoCAD 2016: Extracting floorplans from point clouds using .NET”

  1. I know that this is an old post. Though I did have fun playing with this code. One thing that I changed was getting the xAxis, zAxis and origin from the current ucs instead of always normal. Then when I use the command I would crop the cloud first, so that I only see what I want drawn. I.e. If I was drawing walls I'd crop the chair and desk out of the cloud, then run the command.

    Regards,
    Ben

  2. Hi all,
    Is there also a solution to extract points from RCP imported point cloud?

    1. Hi Mehdi,

      Please post your support requests to the relevant Autodesk forum.

      Thank you,

      Kean

Leave a Reply to Kean Walmsley Cancel reply

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