wang2006zhi 发表于 2024-8-6 12:37:49

直线首尾相连-CAD选择版

本帖最后由 wang2006zhi 于 2024-8-27 13:17 编辑

[CommandMethod("tt222")]
public void Tt222()
{
    using var tr = new DBTrans();
    var tol = new Tolerance(5, 50);
    while (true)
    {
      var sw = Stopwatch.StartNew();
      if (!Env.Editor.SelEnt(out Line line))
            return;
      var queue = new Queue<Line>();
      queue.Enqueue(line);
      var sellines = new List<Line>();
      while (queue.Any())
      {
            var temp = queue.Dequeue();


var ptc = temp.GeometricExtents.GetPointCollection();
if (!Env.Editor.SelEnts(out List<Line> sel, ptc, fil: fil))
    return;




            foreach (var item in sel)
            {
                if (temp.Equals(item))
                  continue;
                if (sellines.Contains(item))
                  continue;
                if (IsLink(temp, item))
                {
                  sellines.Add(item);
                  queue.Enqueue(item);
                }
            }
      }

      if (!sellines.Any())
            continue;
      sellines.ForEach(ent =>
      {
            using (ent.ForWrite())
            {
                ent!.ColorIndex = ent.ColorIndex == 1 ? 2 : 1;
                ent.Draw();
            }
      });
      sw.Stop();
      Env.Editor.WriteMessage("\n TT10用时{0}毫秒", sw.Elapsed.TotalMilliseconds);
    }

    //链接条件
bool IsLink(Line lineA, Line lineB)
    {
      if (lineA.StartPoint.IsEqualTo(lineB.StartPoint, tol)
            || lineA.StartPoint.IsEqualTo(lineB.EndPoint, tol)
            || lineA.EndPoint.IsEqualTo(lineB.StartPoint, tol)
            || lineA.EndPoint.IsEqualTo(lineB.EndPoint, tol)
         )
            return true;
      return false;
    }
}


箭头_Row 发表于 2024-8-6 14:52:43

https://gitee.com/inspirefunction/ifoxcad/blob/v0.7/tests/TestShared/TestCurve.cs

IFOX 封裝了時間監測器,在此測試文件的 57 -61 行里可以看到。

另:
一、 if (!Env.Editor.SelEnt(out Line line)) 中 SelEnt 0.7.1版本的麼看到這個封裝函數,是用的更高版本的 IFOX嗎?

二、temp.StartPoint..GetPointCollection(); 原始APII也麼有這個方法,是自己封裝的嗎?

三、 bool IsLink(Line lineA, Line lineB) 函數的方法建議輸入形參加一個tol,做SDK形式更通用一些。

lelelewfxy 发表于 2024-8-6 17:29:56

很好,学习了,要是加上注释就更好了

你有种再说一遍 发表于 2024-8-6 17:46:03

本帖最后由 你有种再说一遍 于 2024-8-7 02:42 编辑

还得接着优化,考虑使用线程安全邻接表和多线程并行,
因为这样才可以把每次识别一条改为多条,提高吞吐量.

写c#重点是一次性完成全部并且尽可能在一秒内,
而不是像lisp一点一点的处理.

本次的优化建议:


public void Tt222() {
    using var tr = new DBTrans();
    while (true) {
var sw = Stopwatch.StartNew();
// 链式选择,选择图元对象作为起点种子.
if (!Env.Editor.SelEnt(out ObjectId lineId)) return;
// 创建一个队列用于存储待处理的线段...
此处储存line有问题,应该改为id,
一是储存图元的信息熵巨大,Entity和Entity比较会进入内部逻辑而不是id这样的数值比较.
二是不利于后续删除,
三是改为多事务的话,Entity跨事务会错乱,甚至有人不小心在跨事务进行写模式.

// 创建一个列表用于存储符合条件的线段
var sellines = new HashSet<LineType>();

var line = lineId.ToEntity();
var lineType=new LineType(
line.ObjectId,line.StartPoint,line.EndPoint);

var queue = new Queue<LineType>();
queue.Enqueue(lineType);

// 只要队列不为空就继续循环
while (queue.Any()){
    // 不要做无畏的转换,Hash容器可以保证唯一,选择与起点和终点相关的实体加入...按理来说这里用一个ssget就够了...
   HashSet<ObjectId> ids=[];
   var a=Env.Editor.SelEnts(ids);
   if (!a) return;
   var b=Env.Editor.SelEnts(ids);
   if (!b) return;

   // 取出种子线段
   var t = queue.Dequeue();
   foreach (var id in ids) {
      // 当前线段与遍历线段相同则跳过
      if (t.ObjectId==id) continue;
      // 已经添加过该线段则跳过
      var iLine = id.ToEntity();
      var iType=new LineType(
iLine.ObjectId,iLine.StartPoint,iLine.EndPoint);
      if (sellines.Contains(iType)) continue;
      // 有链接关系
      if (t.IsLink(iType)) {
         sellines.Add(iType);
         queue.Enqueue(iType);
      }
    }
}

// 结果列表为空则跳过
if (!sellines.Any()) continue;
// 对结果列表中的每个线段执行操作
sellines.ForEach(id => {
var ent = id.ToEntity();
using (ent.ForWrite()){
// 切换线段的颜色索引
ent!.ColorIndex = ent.ColorIndex == 1 ? 2 : 1;
// 绘制线段
   ent.Draw();
}
});
      // 停止计时
      sw.Stop();
      // 输出操作所用的时间
Env.Editor.WriteMessage($"\n {nameof(Tt222)}用时{sw.Elapsed.TotalMilliseconds}毫秒");
    }
}
public class LineType {
    public ObjectId ObjectId;
    public Point3d StartPoint;
    public Point3d EndPoint;

    // 构造函数
    public LineType(ObjectId objectId, Point3d startPoint, Point3d endPoint) {
      ObjectId = objectId;
      StartPoint = startPoint;
      EndPoint = endPoint;
    }

    // IsLink方法
    public bool IsLink(LineType lineB, Tolerance? tol = null) {
      if (tol == null) {
            tol = new Tolerance(1, 1); // 默认容差值
      }
      return
         StartPoint.IsEqualTo(lineB.StartPoint, tol) ||
          StartPoint.IsEqualTo(lineB.EndPoint, tol) ||
          EndPoint.IsEqualTo(lineB.StartPoint, tol) ||
          EndPoint.IsEqualTo(lineB.EndPoint, tol);
    }
    // 使用hashset或者dictionary重写下面
    // GetHashCode方法
    public override int GetHashCode() {
      unchecked { // 允许溢出,避免不必要的异常处理
            return ObjectId.GetHashCode()
             ^= StartPoint.GetHashCode()
             ^= EndPoint.GetHashCode();
      }
    }

    // Equals方法
    public override bool Equals(object obj) {
    return obj is LineType other
      && ObjectId.Equals(other.ObjectId)
      && StartPoint.Equals(other.StartPoint)
      && EndPoint.Equals(other.EndPoint);
    }
}
}


taiwanfox 发表于 2024-8-7 10:55:22

好東西,謝謝分享,感謝!!!

wang2006zhi 发表于 2024-8-7 13:21:47

本帖最后由 wang2006zhi 于 2024-8-7 14:07 编辑

你有种再说一遍 发表于 2024-8-6 17:46
还得接着优化,考虑使用线程安全邻接表和多线程并行,
因为这样才可以把每次识别一条改为多条,提高吞吐量.
...
非常好的建议,通过构造一个新的类(LineType)用以减少原LINE数据量;但在新建类的时候是否有消耗。。TT21中原直线修改新的类(LineType)测试中用时较之前长。
个人意见:通过直线起终点(如10范围)构造选择集,可以快速过滤掉需要的可能数据,减少循环判断。
var ptcS = temp.StartPoint.GetPointCollection();
var ptcE = temp.EndPoint.GetPointCollection();
if (!Env.Editor.SelEnts(out List<Line> selS, ptcS) & !Env.Editor.SelEnts(out List<Line> selE, ptcE))
   return;
经过实际测试。10万条乱线。你代码用时较多。。。【T2原四叉树】【TT21原CAD选择】【TT22你优化的代码】
TT2用时2478.1483毫秒
请选择对象:
TT2用时977.6429毫秒
请选择对象:
TT2用时1928.7176毫秒
请选择对象:
命令: tt21
请选择对象:
TT21用时1437.1039毫秒
请选择对象:
TT21用时711.9279毫秒
请选择对象:
TT21用时821.3567毫秒
请选择对象:
TT21用时645.9824毫秒
请选择对象:
命令: tt22
请选择对象:
Tt22用时1531.449毫秒
请选择对象:
Tt22用时1604.1684毫秒
请选择对象:
Tt22用时1427.732毫秒
请选择对象:



优化修改后的
[CommandMethod(nameof(Tt22))]
public void Tt22()
{
    using var tr = new DBTrans();
    while (true)
    {
      var sw = Stopwatch.StartNew();
      // 链式选择,选择图元对象作为起点种子.
      if (!Env.Editor.SelEnt(out Line line))
            return;
      // 此处储存line有问题,应该改为id,一是储存图元的信息熵巨大,Entity和Entity比较会进入内部逻辑而不是id这样的数值比较.
      // 二是不利于后续删除,
      // 三是改为多事务的话,Entity跨事务会错乱,甚至有人不小心在跨事务进行写模式.
      // 创建一个队列用于存储待处理的线段...
      // 创建一个列表用于存储符合条件的线段
      var sellines = new HashSet<LineType>();
      var lineType=new LineType(line.ObjectId,line.StartPoint,line.EndPoint);
      var queue = new Queue<LineType>();
      queue.Enqueue(lineType);
      // 只要队列不为空就继续循环
      while (queue.Any())
      {
            // 不要做无畏的转换,Hash容器可以保证唯一,选择与起点和终点相关的实体加入...按理来说这里用一个ssget就够了...
            var temp = queue.Dequeue();
            var ptcS = temp.StartPoint.GetPointCollection();
            var ptcE = temp.EndPoint.GetPointCollection();
            if (!Env.Editor.SelEnts(out HashSet<ObjectId> selS, ptcS) & !Env.Editor.SelEnts(out HashSet<ObjectId> selE, ptcE))
                return;
            var ids = selS.Union(selE);
            // 取出种子线段
            foreach (var id in ids)
            { // 当前线段与遍历线段相同则跳过
            if (temp.ObjectId==id)
                  continue;   
            // 已经添加过该线段则跳过
            var iLine = id.ToEntity<Line>();   
            var iType=new LineType(iLine.ObjectId,iLine.StartPoint,iLine.EndPoint);
            if (sellines.Contains(iType))
                  continue;   
            // 有链接关系
            if (temp.IsLink(iType))
            {
                  sellines.Add(iType);      
                  queue.Enqueue(iType);
            }
            }
      }
      // 结果列表为空则跳过
      if (!sellines.Any())
            continue;
      // 对结果列表中的每个线段执行操作
      sellines.ForEach(id =>
      {
            var ent = id.ObjectId.ToEntity();
            using (ent.ForWrite())
            {// 切换线段的颜色索引
               ent!.ColorIndex = ent.ColorIndex == 1 ? 2 : 1;
               // 绘制线段
               ent.Draw(); }});      
      // 停止计时
      sw.Stop();      
      // 输出操作所用的时间
      Env.Editor.WriteMessage($"\n {nameof(Tt22)}用时{sw.Elapsed.TotalMilliseconds}毫秒");
    }
}

wang2006zhi 发表于 2024-8-7 14:01:14

本帖最后由 wang2006zhi 于 2024-8-7 14:10 编辑

箭头_Row 发表于 2024-8-6 14:52
https://gitee.com/inspirefunction/ifoxcad/blob/v0.7/tests/TestShared/TestCurve.cs

IFOX 封裝了時間 ...
   /// <summary>
    /// 交互模式获取单个实体
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="ed">命令行</param>
    /// <param name="t">实体</param>
    /// <param name="pt">交互点</param>
    /// <param name="msg">提示</param>
    /// <returns>bool</returns>
    public static bool SelEnt<T>(this Editor ed,
      out T t,
      out Point3d pt,
      string msg = "请选择对象:") where T : Entity
    {
      var per = ed.GetEntity("\n " + msg);
      t = null!;
      pt = per.PickedPoint;
      if (per.Status != PromptStatus.OK)
            return false;
      var tr = DBTrans.Top;
      if (tr.GetObject(per.ObjectId) is T result)
      {
            t = result;
            return true;
      }
      return false;
    }

    /// <summary>
    /// 以单点(中心点)构造选择点集
    /// </summary>
    /// <param name="cenpt">中心单点3D</param>
    /// <param name="ex">偏移量</param>
    /// <returns>点集合</returns>
    public static Point3dCollection GetPointCollection(this Point3d cenpt, double ex = 50)
    {
      var pt2d = new Point2d(cenpt.X, cenpt.Y);
      return GetPointCollection(pt2d, ex);
    }

   public static Point3dCollection GetPointCollection(this Point2d cenpt, double ex = 50)
    {
      return new Point3dCollection()
      {
            new Point3d(cenpt.X - 0.5 * ex, cenpt.Y - 0.5 * ex, 0),
            new Point3d(cenpt.X + 0.5 * ex, cenpt.Y - 0.5 * ex, 0),
            new Point3d(cenpt.X + 0.5 * ex, cenpt.Y + 0.5 * ex, 0),
            new Point3d(cenpt.X - 0.5 * ex, cenpt.Y + 0.5 * ex, 0),
      };
    }
      
    此为一个私有函数bool IsLink(Line lineA, Line lineB),并不通用,并且形参中var tol = new Tolerance(5, 50);无法设置默认值。。


你有种再说一遍 发表于 2024-8-7 16:52:11

还能优化的,最起码你现在没有用并行,继续努力

wang2006zhi 发表于 2024-11-1 18:15:07

            #region 精细选取
            
            // var ptcS = dimtemp.DimLinePoint.GetPointCollection(fuz);
            // var ptcE = dimtemp.DimLinePointN.GetPointCollection(fuz);
            // if (!Env.Editor.SelEnts(out List<Dimension> selS, ptcS, fil) &
            //   !Env.Editor.SelEnts(out List<Dimension> selE, ptcE, fil))
            //   return dimInfos;
            // var sel = selS.Union(selE);

            #endregion

            var ptColl = temp.GetRect().GetPointCollection(fuz);
            if (!Env.Editor.SelEnts(out List<Dimension> sel, ptColl, fil))
                return dimInfos;
页: [1]
查看完整版本: 直线首尾相连-CAD选择版