找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1102|回复: 0

[分享] Getting the length of the pipe centrelines in an AutoCAD drawing – Part 3

[复制链接]

已领礼包: 859个

财富等级: 财运亨通

发表于 2014-5-13 22:21:28 来自手机 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 csharp 于 2014-5-14 07:37 编辑

http://through-the-interface.typepad.com/through_the_interface/2014/01/getting-the-length-of-the-pipe-centrelines-in-an-autocad-drawing-part-3.html

Getting the length of the pipe centrelines in an AutoCAD drawing – Part 3
To follow on from yesterday’s post, today we’re taking a look at a more interactive – and iterative – approach to getting the length of a pipe (defined by a surface generated from an imported SAT file, we’re not talking about native Plant 3D objects). This is the second task discussed in the introductory post in this series.
We’re going to add a CTRLINES2 command that asks the user to select pipe section after pipe section, and will only generate the centreline for a newly-selected section if it’s contiguous to the section of pipe that’s being “managed” (i.e. whose length is being counted). The command will report the total length of the pipe via the command-line after each new section is selected.
You can either paste the code from today’s post into that from yesterday’s, or you can just download the updated file.
To help collect our “section” data (and this refers to a length of pipe rather than a cross-section), we’re going to add a new class:
public class PipeSection
{
  public PipeSection()
  {
    _segments = new List<Curve>();
    _start = Point3d.Origin;
    _end = Point3d.Origin;
  }

  private List<Curve> _segments;
  public List<Curve> Segments
  {
    get { return _segments; }
    set { _segments = value; }
  }
  private Point3d _start;
  public Point3d StartPoint
  {
    get { return _start; }
    set { _start = value; }
  }
  private Point3d _end;
  public Point3d EndPoint
  {
    get { return _end; }
    set { _end = value; }
  }

  internal double GetLength()
  {
    double length = 0.0;

    foreach (var segment in _segments)
    {
      length +=
        segment.GetDistanceAtParameter(
          segment.EndParam - segment.StartParam
        );
    }
    return length;
  }

  internal void WriteToBlock(
    Transaction tr, BlockTableRecord btr, int color
  )
  {
    foreach (var ent in _segments)
    {
      btr.AppendEntity(ent);
      tr.AddNewlyCreatedDBObject(ent, true);
      ent.ColorIndex = color;
    }
  }

  internal void Clear()
  {
    foreach (var ent in _segments)
    {
      ent.Dispose();
    }
    _segments.Clear();
  }
}

Then we have the command definition itself (which belongs to the Commands class, of course):
[CommandMethod("CTRLNS2")]
public void GetGeometryCmd()
{
  var doc = Application.DocumentManager.MdiActiveDocument;
  var db = doc.Database;
  var ed = doc.Editor;

  // Ask the user to select the first object (we're
  // not actually limiting selection to only surfaces)

  var peo =
    new PromptEntityOptions(
      "\nSelect first object for centerline extraction"
    );

  var per = ed.GetEntity(peo);
  if (per.Status != PromptStatus.OK)
    return;

  bool cont = true;
  double total = 0.0;
  var start = Point3d.Origin;
  var end = Point3d.Origin;
  bool first = true;

  while (cont)
  {
    using (var tr = db.TransactionManager.StartTransaction())
    {
      // Get the modelspace for write

      var bt =
        (BlockTable)tr.GetObject(
          db.BlockTableId,
          OpenMode.ForRead
        );
      var ms =
        (BlockTableRecord)tr.GetObject(
          bt[BlockTableRecord.ModelSpace],
          OpenMode.ForWrite
        );

      // Get the selected object, extract it's primitives,
      // then try to link them into sections

      var ent =
        (Entity)tr.GetObject(per.ObjectId, OpenMode.ForRead);
      var ents = ExtractPathPrimitives(ent);
      var sections = LinkPrimitivesIntoSections(ents);

      // Loop through the sections and attempt to stitch
      // them with any that have been generated already

      foreach (var section in sections)
      {
        bool connected = false;
        var newStart = start;
        var newEnd = end;

        if (first)
        {
          newStart = section.StartPoint;
          newEnd = section.EndPoint;
          first = false;
          connected = true;
        }
        else
        {
          if (SamePoint(start, section.StartPoint))
          {
            connected = true;
            newStart = section.EndPoint;
          }
          else if (SamePoint(start, section.EndPoint))
          {
            connected = true;
            newStart = section.StartPoint;
          }
          else if (SamePoint(end, section.StartPoint))
          {
            connected = true;
            newEnd = section.EndPoint;
          }
          else if (SamePoint(end, section.EndPoint))
          {
            connected = true;
            newEnd = section.StartPoint;
          }
        }

        if (!connected)
        {
          ed.WriteMessage(
            "\nSkipping non-contiguous section."
          );
          section.Clear();
        }
        else
        {
          start = newStart;
          end = newEnd;

          total += section.GetLength();

          ed.WriteMessage("\nTotal pipe length: {0}", total);

          section.WriteToBlock(tr, ms, 1);
        }
      }

      tr.Commit();
    }

    // Change the prompt and ask again, looping if appropriate

    peo.Message = "\nSelect next object";
    per = ed.GetEntity(peo);
    cont = per.Status == PromptStatus.OK;
  }
}

And then – also in the Commands class – we’ll add a few helper functions to stitch segments into one or more sections:
private List<PipeSection> LinkPrimitivesIntoSections(
  List<Curve> ents
)
{
  var sections = new List<PipeSection>();

  while (ents.Count > 0)
  {
    sections.Add(LinkSection(ents));
  }

  return sections;
}

private static PipeSection LinkSection(List<Curve> ents)
{
  var section = new PipeSection();

  // Use a flag to see whether any new segments were added in
  // the last pass

  bool foundNew = true;
  bool first = true;

  while (foundNew)
  {
    foundNew = false;

    // Loop through the entities, looking for any with the
    // same start- or end-point (but not both)

    for (int i = 0; i < ents.Count; i++)
    {
      var cur = ents;

      bool connected = false;
      var newStart = section.StartPoint;
      var newEnd = section.EndPoint;

      // The first curve in our list gets special treatment

      if (first)
      {
        newStart = cur.StartPoint;
        newEnd = cur.EndPoint;
        connected = true;
        first = false;
      }
      else
      {
        // The others we check more carefully

        if (
          SamePoint(section.StartPoint, cur.StartPoint) &&
          !SamePoint(section.EndPoint, cur.EndPoint)
        )
        {
          connected = true;
          newStart = cur.EndPoint;
        }
        else if (
          SamePoint(section.StartPoint, cur.EndPoint) &&
          !SamePoint(section.EndPoint, cur.StartPoint)
        )
        {
          connected = true;
          newStart = cur.StartPoint;
        }
        else if (
          SamePoint(section.EndPoint, cur.StartPoint) &&
          !SamePoint(section.StartPoint, cur.EndPoint)
        )
        {
          connected = true;
          newEnd = cur.EndPoint;
        }
        else if (
          SamePoint(section.EndPoint, cur.EndPoint) &&
          !SamePoint(section.StartPoint, cur.StartPoint)
        )
        {
          connected = true;
          newEnd = cur.StartPoint;
        }
      }

      // If we found a connection...

      if (connected)
      {
        // Update the section start- and end-points

        section.StartPoint = newStart;
        section.EndPoint = newEnd;

        // Remove the curve from the list and add it to our
        // section's segments

        ents.RemoveAt(i--);
        section.Segments.Add(cur);
        foundNew = true;
      }
    }
  }
  return section;
}

This code still isn’t checking for overlap, so it’s admittedly a little fragile: it will often tell you that the newly-selected section isn’t contiguous, for instance. To really be useable it would need to handle overlapping segments more elegantly. If that were done, then I think the approach would rival the one shown yesterday (i.e. CTRLINES, OVERKILL then GSL). For now yesterday’s still seems the simplest – and ultimately most effective – approach to work with.
I’d be curious to get feedback from anyone who actually tries the code shown in the last two posts and – ideally – gets the chance to compare the results with those created manually. I do think it could prove to be very useful, but no doubt some tweaks will be needed – I’ve only dealt with the edge cases shown in a small sample of piping data, for instance: there may well be others that this code works less well against.


论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2025-1-6 01:35 , Processed in 0.371627 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表