cad.net 封装jig

重构了一下自己的几处jig代码,发现可以抽象出一些公共内容,不单纯每次写类继承(麻烦),
提供出来给大家.

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Geometry;
using GrxCAD.GraphicsInterface;
using Acap = GrxCAD.ApplicationServices.Application;
#endif

using System;
using System.Collections.Generic;
using System.Linq;

/*  封装jig
 *  20211216 加入块表时候做一个差集,剔除临时图元
 *  20211209 补充正交变量设置和回收设置
 *  作者: 惊惊⎛⎝◕⏝⏝◕。⎠⎞ ⎛⎝≥⏝⏝0⎠⎞ ⎛⎝⓿⏝⏝⓿。⎠⎞ ⎛⎝≥⏝⏝≤⎠⎞
 *  博客: https://www.cnblogs.com/JJBox/p/15650770.html
 *
 *  例子1:
 *  var ptjig = new Jig();
 *  ptjig.SetOptions(Point3d.Origin);
 *  var pr = ptjig.Drag();
 *  if (pr.Status != PromptStatus.OK)
 *      return null;
 *
 *  例子2:
 *  var ppo1 = new PromptPointOptions(Environment.NewLine + "输入矩形角点1:<空格退出>")
 *  {
 *      AllowArbitraryInput = true,//任意输入
 *      AllowNone           = true //允许回车
 *  };
 *  var ppr1 = ed.GetPoint(ppo1);//用户点选
 *  if (ppr1.Status != PromptStatus.OK)
 *      return;
 *  var getPt = ppr1.Value;
 *
 *  var recEntityJig = new Jig((mousePoint, drawEntitys) => {
 *      #region 画柜子图形
 *      double length = Math.Abs(getPt.X - mousePoint.X);
 *      double high = Math.Abs(getPt.Y - mousePoint.Y);
 *      var ent = AddRecToEntity(Point3d.Origin, new Point3d(length, high, 0));
 *      drawEntitys.Add(ent);
 *      #endregion
 *  });
 *  recEntityJig.SetOptions("指定矩形角点:", new Dictionary<string, string>() { { "Z", "中间(Z)" } );
 *
 *  bool flag = true;
 *  while (flag)
 *  {
 *      var pr = recEntityJig.Drag();
 *      if (string.IsNullOrEmpty(pr.StringResult))//在无输入的时候会等于空
 *          flag = false;
 *      else
 *      {
 *          switch (pr.StringResult.ToUpper()) //注意cad保留 https://www.cnblogs.com/JJBox/p/10224631.html
 *          {
 *              case "Z":
 *                  ed.WriteMessage("\n您触发了z关键字");
 *                  break;
 *              case " ":
 *                  flag = false;//空格结束
 *                  break;
 *          }
 *      }
 *  }
 *  //开启事务之后,图元加入数据库
 *  db.Action(tr=>{
 *     recEntityJig.AddEntityToMsPs(tr);
 *  });
 */

namespace JoinBox
{
    public delegate void WorldDrawEvent(WorldDraw draw);
    public class Jig : DrawJig
    {
        #region 成员
        /// <summary>
        /// 事件:默认是图元刷新,其余的:亮显/暗显等等工作自由补充
        /// </summary>
        public event WorldDrawEvent WorldDrawEvent;
        /// <summary>
        /// 最后的鼠标点,用来确认长度
        /// </summary>
        public Point3d MousePointWcsLast;
        /// <summary>
        /// 最后的图元,用来生成
        /// </summary>
        public Entity[] Entitys => _drawEntitys.ToArray();

        List<Entity> _drawEntitys;//重复生成的图元,放在这里刷新
        Autodesk.AutoCAD.Geometry.Tolerance _tolerance;
        Action<Point3d, List<Entity>> _action;
        JigPromptPointOptions _options;
        const string _orthomode = "orthomode";
        bool _systemVariablesOrthomode = false; //正交修改
        #endregion

        #region 构造
        /// <summary>
        /// 在界面绘制图元
        /// </summary>
        /// <param name="action">
        /// 用来频繁执行的回调: <see langword="Point3d"/>鼠标点,<see langword="List"/>加入显示图元的容器
        /// </param>
        /// <param name="tolerance">鼠标移动的容差</param>
        public Jig(Action<Point3d, List<Entity>> action = null, double tolerance = 1e-6)
        {
            _action      = action;
            _tolerance   = new(tolerance, tolerance);
            _drawEntitys = new();
        }
        #endregion

        #region 方法
        /// <summary>
        /// 鼠标配置:基点
        /// </summary>
        /// <param name="basePoint">基点</param>
        /// <param name="msg">提示信息</param>
        /// <param name="cursorType">光标绑定</param>
        /// <param name="orthomode">正交开关</param>
        public JigPromptPointOptions SetOptions(Point3d basePoint,
            CursorType cursorType = CursorType.RubberBand,
            string msg = "点选第二点",
            bool orthomode = false)
        {
            if (orthomode && CadSystem.Getvar(_orthomode) != "1")
            {
                CadSystem.Setvar(_orthomode, "1");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html
                _systemVariablesOrthomode = true;
            }
            var tmp = new JigPromptPointOptions(Environment.NewLine + msg)
            {
                Cursor = cursorType,   //光标绑定
                UseBasePoint = true,   //基点打开
                BasePoint = basePoint, //基点设定

                //用户输入控件:  由UCS探测用 | 接受三维坐标
                UserInputControls =
                    UserInputControls.GovernedByUCSDetect |
                    UserInputControls.Accept3dCoordinates
            };
            _options = tmp;
            return _options;
        }

        /// <summary>
        /// 鼠标配置:提示信息,关键字
        /// </summary>
        public JigPromptPointOptions SetOptions(string msg, Dictionary<string, string> keywords = null)
        {
            var tmp = new JigPromptPointOptions(Environment.NewLine + msg)
            {
                //用户输入控件:  由UCS探测用 | 接受三维坐标
                UserInputControls =
                      UserInputControls.GovernedByUCSDetect |
                      UserInputControls.Accept3dCoordinates
            };
            if (keywords != null)
                foreach (var item in keywords)
                    tmp.Keywords.Add(item.Key, item.Key, item.Value);

            tmp.Keywords.Add(" ", " ", "<空格退出>"); //要放最后,才能优先触发其他关键字
            _options = tmp;
            return _options;
        }

        /// <summary>
        /// 鼠标配置:自定义
        /// </summary>
        /// <param name="action"></param>
        public void SetOptions(Action<JigPromptPointOptions> action)
        {
            var tmp = new JigPromptPointOptions();
            action.Invoke(tmp);
            _options = tmp;
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <returns></returns>
        public PromptResult Drag()
        {
            //jig功能必然是当前前台文档,所以封装内部更好调用
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var ed = doc.Editor;
            var dr = ed.Drag(this);
            if (_systemVariablesOrthomode)
                CadSystem.Setvar(_orthomode, "0");//1正交,0非正交 //setvar: https://www.cnblogs.com/JJBox/p/10209541.html
            return dr;
        }

        /// <summary>
        /// 最后一次的图元加入数据库
        /// </summary>
        /// <param name="tr">事务</param>
        /// <param name="removeEntity">不生成的图元用于排除,例如刷新时候的提示文字</param>
        /// <returns></returns>
        public IEnumerable<ObjectId> AddEntityToMsPs(Transaction tr,
            IEnumerable<Entity> removeEntity = null)
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ids = new List<ObjectId>();
            IEnumerable<Entity> ents = Entitys;
            if (removeEntity != null)
                ents = Entitys.Except(removeEntity);
            foreach (var item in ents)
                ids.Add(tr.AddEntityToMsPs(db, item));// https://www.cnblogs.com/JJBox/p/14300098.html
            return ids;
        }
        #endregion

        #region 重写
        /// <summary>
        /// 鼠标频繁采点
        /// </summary>
        /// <param name="prompts"></param>
        /// <returns>返回状态:令频繁刷新结束</returns>
        protected override SamplerStatus Sampler(JigPrompts prompts)
        {
            if (_options == null)
                throw new ArgumentNullException(nameof(_options));

            var pro = prompts.AcquirePoint(_options);
            if (pro.Status == PromptStatus.Keyword)
                return SamplerStatus.OK;
            if (pro.Status != PromptStatus.OK)
                return SamplerStatus.Cancel;

            //上次鼠标点不同(一定要这句,不然图元刷新太快会看到奇怪的边线)
            var mousePointWcs = pro.Value;

            //==        是比较新建类的哈希值,
            //IsEqualTo 是方形判断(仅加法),
            //Distance  是圆形判断(会求平方根,使用了牛顿迭代),
            //一定要有这样细微的心,不然在大量数据(十万以上/频繁刷新)面前会显得非常慢.
            if (mousePointWcs.IsEqualTo(MousePointWcsLast, _tolerance))
                return SamplerStatus.NoChange;

            //上次循环的缓冲区图元清理,否则将会在vs输出遗忘 Dispose
            foreach (var item in _drawEntitys)
                item.Dispose();
            _drawEntitys.Clear();

            //委托把容器扔出去接收新创建的图元,然后给重绘更新
            _action?.Invoke(mousePointWcs, _drawEntitys);

            MousePointWcsLast = mousePointWcs;
            return SamplerStatus.OK;
        }

        /// <summary>
        /// 重绘图形
        /// </summary>
        protected override bool WorldDraw(WorldDraw draw)
        {
            if (_drawEntitys.Count != 0)
            {
                //重绘是UI异步线程,所以必须生成一份副本
                var lst = _drawEntitys.ToArray();
                foreach (var item in lst)
                    draw.Geometry.Draw(item);
            }

            WorldDrawEvent?.Invoke(draw);
            return true;
        }
        #endregion
    }
}

(完)

原文地址:https://www.cnblogs.com/JJBox/p/15650770.html