cad.net 仿lisp函数专篇

相关阅读

cad.net 定义lisp与发送同步命令

仿mapcar函数

提供:雪山飞狐

/// <summary>
/// 仿lisp的mapcar函数
/// </summary> 
public static IEnumerable<TR> Mapcar<T1, T2, TR>(IEnumerable<T1> lst1, IEnumerable<T2> lst2, Func<T1, T2, TR> func)
{
    var itor1 = lst1.GetEnumerator();
    var itor2 = lst2.GetEnumerator();
    while (itor1.MoveNext() && itor2.MoveNext())
        yield return func(itor1.Current, itor2.Current);
}

调用

public class TestMapcar
{
    [CommandMethod("tt")]
    public void tt()
    {
        Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;

        var st1 = new List<double> { 1, 2, 3, 4 };
        var st2 = new List<double> { 1, 2, 3, 4 };
        var numbers = Mapcar(st1, st2, (a, b) => a + b);

        foreach (var item in numbers)
        {
            ed.WriteMessage("\n" + item.ToString());
        }
    }
}
=>输出: 2 4 6 8

您可能还需要C#中的yield关键字的参考.
避免判断yield中使用linq的Count()>0

仿vlax-ldata-get函数

先看lisp如何设置和获取一个词典对象:

词典上面,设置: 词典名 mydict

(vlax-ldata-put "mydict" "mykey" "Mumbo Dumbo")
=>"Mumbo Dumbo"

词典上面,获取: 词典名 mydict

(vlax-ldata-get "mydict" "mykey")
=>"Mumbo Dumbo"

图元上面,设置:

(vlax-ldata-put (car (entsel)) "mykey" "Mumbo Dumbo")
=>"Mumbo Dumbo"

图元上面,获取:

(vlax-ldata-get (car (entsel)) "mykey")
=>"Mumbo Dumbo"

图元上面,删掉:

(vlax-ldata-delete (car (entsel)) "mykey")
=>T

动态调用dll

获取图元对象的词典:参考文章

由于DllImport要写死dll名称,所以采取了一种动态调用dll内部方法的方式来进行优化:

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.Runtime.InteropServices;
using static Win32API.WinApi;

public struct AdsName
{
    public IntPtr name1;
    public IntPtr name2;
}

namespace JoinBox
{
    public class AcdbAdsHelper
    {
#if NET35
        //Acad2008的AcdbEntGetX参数必须加ref,
        //而Acad2020不用(中间版本没有测试哦)..所以为了统一,就全部加了
        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acdbEntGet")]
        public static extern IntPtr AcdbEntGet(ref AdsName objName);

        [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acdbEntGetX")]
        public static extern IntPtr AcdbEntGetX(ref AdsName objName, IntPtr app);
#else
        [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl,EntryPoint = "acdbEntGet")]
        public static extern IntPtr AcdbEntGet(ref AdsName objName);

        [DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl,EntryPoint = "acdbEntGetX")]
        public static extern IntPtr AcdbEntGetX(ref AdsName objName, IntPtr app);
#endif


#if false  //如果版本号 acdb17.acdb18.acdb19 不确定,那么就采用反射调用
        [DllImport("acdb19.dll", CallingConvention = CallingConvention.Cdecl,
                   EntryPoint = "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AAY01JVAcDbObjectId@@@Z")]
        static extern ErrorStatus AcdbGetAdsName32(out AdsName objName, ObjectId id);

        [DllImport("acdb19.dll", CallingConvention = CallingConvention.Cdecl,
               EntryPoint = "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AEAY01_JVAcDbObjectId@@@Z")]
        static extern ErrorStatus AcdbGetAdsName64(out AdsName objName, ObjectId id);
#endif

        public static class Acdb
        {
            static string Version()
            {
                var acadver = Acap.GetSystemVariable("acadver").ToString();
                var ver = acadver.Substring(0, 2);
                return ver;
            }
            public static string AcdbDll()
            {
                return "acdb" + Version() + ".dll";
            }
        }

        /// <summary>
        /// 将ObjectId转为AdsName
        /// </summary>
        /// <param name="name"></param>
        /// <param name="objId"></param>
        /// <returns></returns>
        public static ErrorStatus AcdbGetAdsName(out AdsName name, ObjectId objId)
        {
            var dllName = Acdb.AcdbDll();
            var hModule = GetModuleHandle(dllName);
            if (hModule == IntPtr.Zero)
                throw new System.Exception("AcdbGetAdsName找不到模块:" + dllName);

            string funcName;
#if false
            // DllImport调用时候就这么直接调用就好了
            // 但是为了不每个版本都写一个预处理,所以才去动态调用的方法.
            if (Marshal.SizeOf(IntPtr.Zero) > 4)
                return AcdbGetAdsName64(out name, objId);
            else
                return AcdbGetAdsName32(out name, objId);
#else
            // 但是动态调用就要这样
            if (Marshal.SizeOf(IntPtr.Zero) > 4)
                funcName =
                    "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AEAY01_JVAcDbObjectId@@@Z";
            else
                funcName =
                    "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AAY01JVAcDbObjectId@@@Z";
#endif
            //函数指针
            var funcAdress = GetProcAddress(hModule, funcName);
            if (funcAdress == IntPtr.Zero)
                throw new System.Exception("AcdbGetAdsName没有找到这个函数的入口点:" + funcAdress);

            //利用委托调用函数指针,从而实现对方法的调用
            var delega = Marshal.GetDelegateForFunctionPointer(funcAdress,
                                 typeof(DelegateAcdbGetAdsName)) as DelegateAcdbGetAdsName;
            return delega(out name, objId);
        }

        //委托,调用函数指针用
        delegate ErrorStatus DelegateAcdbGetAdsName(out AdsName name, ObjectId objId);
    }

    public class CmdTest_GetDictClass
    {
        [CommandMethod("CmdTest_GetDict")]
        public void CmdTest_GetDict()
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            ed.WriteMessage("像lisp一样获取ldata");

            var promptEntity = ed.GetEntity("\n请选择图元:");
            if (promptEntity.Status != PromptStatus.OK)
                return;

            doc.Database.Action(tr => {
                var obj = tr.GetObject(promptEntity.ObjectId, OpenMode.ForRead);

                //获取对象的扩展字典
                if (!obj.ExtensionDictionary.IsOk())
                    return;

                var objExDic = tr.GetObject(obj.ExtensionDictionary, OpenMode.ForRead) as DBDictionary;
                if (objExDic == null)
                    return;

                var at = objExDic.GetAt("mykey");              //词条id
                var bp = tr.GetObject(at, OpenMode.ForRead);   //词条对象

                var rb = new ResultBuffer();
                AcdbAdsHelper.AcdbGetAdsName(out AdsName m_EName, bp.ObjectId);
                var getx = AcdbAdsHelper.AcdbEntGetX(ref m_EName, rb.UnmanagedObject);
                if (getx == IntPtr.Zero)
                    return;
                rb = ResultBuffer.Create(getx, true);
                ed.WriteMessage(rb.ToString());
            });
        }
    }
}

一些必要的win32API:

/// <summary>
/// 获取一个应用程序或dll的模块句柄
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string name);

/// <summary>
/// 获取要引入的函数,将符号名或标识号转换为DLL内部地址
/// </summary>
/// <param name="hModule">exe/dll句柄</param>
/// <param name="procName">接口名</param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

得到结果:

((-1,(-2173632))(0,VLO-VL)(5,1E0)(102,{ACAD_REACTORS)(330,(-2173640))(102,})(330,(-2173640))(100,vlo_VL)(90,-64512)(91,13)(92,0)(300,"Mumbo Dumbo"))

仿ssnamex函数

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

using System;
using System.Diagnostics;

namespace JoinBox
{
    public class CmdTest_SSNamexClass
    {
        [CommandMethod("CmdTest_SSNamex", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public void CmdTest_SSNamex()
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            ed.WriteMessage(Environment.NewLine + "****惊惊连盒-仿lisp的ssnamex测试选择集的点选位置和点选方式");

            var filList = new TypedValue[]
            {
               // new TypedValue((int)DxfCode.Start, "INSERT")
            };
            var filter = new SelectionFilter(filList);
            var pso = new PromptSelectionOptions
            {
                RejectObjectsOnLockedLayers = true, //不选择锁定图层对象
                AllowDuplicates             = true, //不允许重复选择
            };
            var ssPsr = ed.GetSelection(pso, filter);//手选
            if (ssPsr.Status != PromptStatus.OK)
                return;

            foreach (var vaItem in ssPsr.Value)
            {
                if (vaItem is CrossingOrWindowSelectedObject cows)//框选方式
                {
                    foreach (var ppd in cows.GetPickPoints())
                        Debug.WriteLine(ppd.PointOnLine);//ppd内还有内容
                }
                else if (vaItem is PickPointSelectedObject pps)//点选方式
                    Debug.WriteLine(pps.PickPoint);            //pps内还有内容
            }

            //获取所有的id
            //foreach (var item in ssPsr.Value.GetObjectIds())
            //    Debug.WriteLine(item);
        }
    }
}

(未完待续)

posted @ 2020-03-06 15:16  惊惊  阅读(1084)  评论(0编辑  收藏  举报