macad.core解析exchange

1.exchange

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using Macad.Core;

namespace Macad.Core
{
    // 交换器注册表
    public static class ExchangeRegistry
    {
        // 交换器字典
        static readonly Dictionary<Type, List<IExchanger>> _Exchangers = new Dictionary<Type, List<IExchanger>>();

        // 设置面板字典
        static readonly Dictionary<Type, IExchangerSettingsPanelCreator> _ExchangerSettingPanels = new Dictionary<Type, IExchangerSettingsPanelCreator>();

        // 枚举和创建区域

        // 枚举指定类型的所有交换器
        public static IEnumerable<T> EnumerateExchanger<T>() where T : IExchanger
        {
            if (_Exchangers.TryGetValue(typeof(T), out var list))
                return list.Cast<T>();
            return new List<T>();
        }

        // 查找指定类型的交换器
        public static T FindExchanger<T>(string extension) where T : class, IExchanger
        {
            if (!_Exchangers.TryGetValue(typeof(T), out var list))
                return null;

            var lowerExtension = extension.ToLower();
            return list.FirstOrDefault(exchanger => exchanger.Extensions.Contains(lowerExtension)) as T;
        }

        // 判断指定类型的交换器是否支持导出到剪贴板
        public static bool CanExportToClipboard<T>() where T : IExchanger
        {
            if (!_Exchangers.TryGetValue(typeof(T), out var list))
                return false;
            return list.Any(exchanger => exchanger.CanExportToClipboard());
        }

        // 判断指定类型的交换器是否支持从剪贴板导入
        public static bool CanImportFromClipboard<T>(Clipboard clipboard) where T : IExchanger
        {
            if (!_Exchangers.TryGetValue(typeof(T), out var list))
                return false;
            return list.Any(exchanger => exchanger.CanImportFromClipboard(clipboard));
        }

        // 注册区域

        // 添加交换器到字典
        static void _AddExchanger(IExchanger exchanger, Type interfaceType)
        {
            if (!_Exchangers.TryGetValue(interfaceType, out var list))
            {
                list = new List<IExchanger>();
                _Exchangers.Add(interfaceType, list);
            }

            if (list.Exists(ex => ex.GetType() == exchanger.GetType()))
            {
                Messages.Warning($"Exchanger {exchanger.GetType().FullName} already registered for type {interfaceType.Name}");
                return;
            }

            list.Add(exchanger);
        }

        // 注册交换器
        public static void Register(IExchanger exchanger)
        {
            var interfaces = exchanger.GetType().GetInterfaces();

            foreach (var interfaceType in interfaces)
            {
                _AddExchanger(exchanger, interfaceType);
            }
        }

        // 注册设置面板
        public static void Register(IExchangerSettingsPanelCreator panelCreator)
        {
            if (_ExchangerSettingPanels.TryGetValue(panelCreator.ExchangerType, out var firstPanel))
            {
                Messages.Error($"Multiple setting panels registered for exchanger {panelCreator.ExchangerType.FullName}: {firstPanel.GetType().FullName} / {panelCreator.GetType().FullName}.");
                return;
            }

            _ExchangerSettingPanels.Add(panelCreator.ExchangerType, panelCreator);
        }

        // 查找交换器设置面板
        public static IExchangerSettingsPanelCreator FindExchangerSettingsPanel(IExchanger exchanger)
        {
            if (_ExchangerSettingPanels.TryGetValue(exchanger.GetType(), out var panelCreator))
            {
                return panelCreator;
            }

            return null;
        }

        // 设置区域

        // 保存设置
        public static void SaveSettings()
        {
            try
            {
                var settingsList = new Dictionary<string, IExchangerSettings>();
                var exchangers = _Exchangers.Values.SelectMany(list => list).Distinct();
                foreach (var exchanger in exchangers)
                {
                    if (exchanger.Settings != null)
                        settingsList.Add(exchanger.GetType().Name, exchanger.Settings);
                }

                CoreContext.Current.SaveLocalSettings("ExchangerSettings", settingsList);
            }
            catch (Exception)
            {
                return;
            }
        }

        // 加载设置
        public static void LoadSettings()
        {
            try
            {
                var settingsList = CoreContext.Current.LoadLocalSettings<Dictionary<string, IExchangerSettings>>("ExchangerSettings");
                if (settingsList == null || settingsList.Count == 0)
                    return;

                var exchangers = _Exchangers.Values.SelectMany(list => list).Distinct();
                foreach (var exchanger in exchangers)
                {
                    if (settingsList.TryGetValue(exchanger.GetType().Name, out var settingObj))
                        exchanger.Settings = settingObj;
                }
            }
            catch (Exception)
            {
                return;
            }
        }
    }
}

这段代码实现了一个交换器注册表,其中包含了注册、枚举、查找、设置保存和加载等功能:

  • 注册区域:包括注册交换器和注册设置面板。
  • 枚举和创建区域:包括枚举指定类型的所有交换器和查找指定类型的交换器。
  • 设置区域:包括保存设置和加载设置。
cs 复制代码
using System;
using System.Collections.Generic;
using Macad.Common.Serialization;

namespace Macad.Core
{
    // 交换器设置接口
    [SerializeType]
    public interface IExchangerSettings
    {}

    //--------------------------------------------------------------------------------------------------

    // 交换器接口
    public interface IExchanger
    {
        string Description { get; }         // 获取交换器描述
        string[] Extensions { get; }        // 获取支持的文件扩展名
        IExchangerSettings Settings { get; set; }   // 获取或设置交换器设置

        // 判断是否可以导出到剪贴板
        bool CanExportToClipboard();
        
        // 判断是否可以从剪贴板导入
        bool CanImportFromClipboard(Clipboard clipboard);
    }

    //--------------------------------------------------------------------------------------------------

    // 实体导出器接口
    public interface IBodyExporter : IExchanger
    {
        // 导出实体
        bool DoExport(string fileName, IEnumerable<Topology.Body> bodies);
    }

    //--------------------------------------------------------------------------------------------------

    // 实体导入器接口
    public interface IBodyImporter : IExchanger
    {
        // 导入实体
        bool DoImport(string fileName, out IEnumerable<Topology.Body> bodies);
    }

    //--------------------------------------------------------------------------------------------------

    // 草图导出器接口
    public interface ISketchExporter : IExchanger
    {
        // 导出草图到文件
        bool DoExport(string fileName, Shapes.Sketch sketch);

        // 导出草图到剪贴板
        bool DoExport(Clipboard clipboard, Shapes.Sketch sketch);
    }

    //--------------------------------------------------------------------------------------------------

    // 草图导入器接口
    public interface ISketchImporter : IExchanger
    {
        // 从文件导入草图
        bool DoImport(string fileName, out IDictionary<int, Occt.Pnt2d> points, out IDictionary<int, Shapes.SketchSegment> segments, 
            out IEnumerable<Shapes.SketchConstraint> constraints);
        
        // 从剪贴板导入草图
        bool DoImport(Clipboard clipboard, out IDictionary<int, Occt.Pnt2d> points, out IDictionary<int, Shapes.SketchSegment> segments, 
            out IEnumerable<Shapes.SketchConstraint> constraints);
    }

    //--------------------------------------------------------------------------------------------------

    // 绘图导出器接口
    public interface IDrawingExporter : IExchanger
    {
        // 导出绘图
        bool DoExport(string fileName, Drawing.Drawing drawing);
    }

    //--------------------------------------------------------------------------------------------------

    // 交换器设置面板创建器接口
    public interface IExchangerSettingsPanelCreator
    {
        Type ExchangerType { get; }    // 获取交换器类型
        object CreatePanel<T>(IExchanger exchanger);   // 创建交换器设置面板
    }

    //--------------------------------------------------------------------------------------------------

}

这段代码定义了一系列交换器接口,包括实体导入导出器、草图导入导出器、绘图导出器以及交换器设置面板创建器。每个接口都定义了相应的方法,用于实现具体的导入导出操作。

3.framework

cs 复制代码
using System;
using Macad.Occt;

namespace Macad.Core
{
    // Bnd_Box 类型的扩展方法
    public static class BndBoxExtensions
    {
        // 获取边界框的中心点
        public static Pnt Center(this Bnd_Box box)
        {
            double xMin = 0, yMin = 0, zMin = 0, xMax = 0, yMax = 0, zMax = 0;
            box.Get(ref xMin, ref yMin, ref zMin, ref xMax, ref yMax, ref zMax );
            return new Pnt( 
                xMin + (xMax - xMin) / 2, 
                yMin + (yMax - yMin) / 2, 
                zMin + (zMax - zMin) / 2 );
        }

        //--------------------------------------------------------------------------------------------------

        // 获取边界框的尺寸
        public static (double X, double Y, double Z) Extents(this Bnd_Box box)
        {
            double xMin = 0, yMin = 0, zMin = 0, xMax = 0, yMax = 0, zMax = 0;
            box.Get(ref xMin, ref yMin, ref zMin, ref xMax, ref yMax, ref zMax );

            return (Math.Abs(xMax - xMin), Math.Abs(yMax - yMin), Math.Abs(zMax - zMin));
        }

        //--------------------------------------------------------------------------------------------------

        // 获取边界框的最小和最大坐标值
        public static (double minX, double minY, double minZ, double maxX, double maxY, double maxZ) MinMax(this Bnd_Box box)
        {
            double xMin = 0, yMin = 0, zMin = 0, xMax = 0, yMax = 0, zMax = 0;
            box.Get(ref xMin, ref yMin, ref zMin, ref xMax, ref yMax, ref zMax );

            return (xMin, yMin, zMin, xMax, yMax, zMax);
        }

        //--------------------------------------------------------------------------------------------------

    }
}

这段代码定义了针对 Bnd_Box 类型的扩展方法。这些方法使得可以方便地获取边界框的中心点、尺寸以及最小和最大坐标值。

cs 复制代码
using System;
using Macad.Occt;

namespace Macad.Core
{
    // gp 类型的扩展方法
    public static class gpExtensions
    {
        #region Pnt

        // 将点坐标各维度取整
        public static Pnt Rounded(this Pnt pnt)
        {
            return new Pnt(pnt.X.Round(), pnt.Y.Round(), pnt.Z.Round());
        }
        
        //--------------------------------------------------------------------------------------------------

        // 交换两个点的值
        public static void Swap(this ref Pnt value, ref Pnt other)
        {
            Pnt temp = value;
            value = other;
            other = temp;
        }

        //--------------------------------------------------------------------------------------------------

        // 缩放点坐标
        public static Pnt Scaled(this Pnt pnt, double scale)
        {
            return new Pnt(pnt.X * scale, pnt.Y * scale, pnt.Z * scale);
        }
        
        //--------------------------------------------------------------------------------------------------

        #endregion
        
        #region Pnt2d

        // 将二维点坐标各维度取整
        public static Pnt2d Rounded(this Pnt2d pnt)
        {
            return new (pnt.X.Round(), pnt.Y.Round());
        }

        //--------------------------------------------------------------------------------------------------

        // 交换两个二维点的值
        public static void Swap(this ref Pnt2d value, ref Pnt2d other)
        {
            Pnt2d temp = value;
            value = other;
            other = temp;
        }

        //--------------------------------------------------------------------------------------------------

        // 两点之间进行线性插值
        public static Pnt2d Lerped(this Pnt2d value, Pnt2d other, double amount)
        {
            return new Pnt2d(value.X.Lerp(other.X, amount), value.Y.Lerp(other.Y, amount));
        }

        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Vec2d

        // 两向量之间进行线性插值
        public static Vec2d Lerped(this Vec2d value, Vec2d other, double amount)
        {
            return new Vec2d(value.X.Lerp(other.X, amount), value.Y.Lerp(other.Y, amount));
        }

        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Dir

        // 两个方向之间进行球形线性插值
        public static Dir Slerped(this Dir value, Dir other, double amount)
        {
            double dotProduct = value.Dot(other);

            // 确定两个向量之间的最短旋转方向
            if (dotProduct < 0)
            {
                other.Reverse();
                dotProduct = -dotProduct;
            }

            double angle = Math.Acos(dotProduct);

            if (Math.Abs(angle) < 0.000001f)
            {
                return value;
            }

            double invSinAngle = 1f / Math.Sin(angle);
            double factorA = Math.Sin((1f - amount) * angle) * invSinAngle;
            double factorB = Math.Sin(amount * angle) * invSinAngle;

            return new Dir(value.ToVec(factorA) + other.ToVec(factorB));
        }

        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Pln

        // 获取平面的旋转四元数
        public static Quaternion Rotation(this Pln plane)
        {
            var mat = new Mat(
                plane.XAxis.Direction.Coord,
                plane.YAxis.Direction.Coord,
                plane.Axis.Direction.Coord);
            return new Quaternion(mat);
        }

        //--------------------------------------------------------------------------------------------------

        // 检查两个平面是否相等
        public static bool IsEqual(this Pln pln1, Pln pln2)
        {
            return pln1.Location.IsEqual(pln2.Location, Double.Epsilon)
                   && pln1.Rotation().IsEqual(pln2.Rotation());
        }

        //--------------------------------------------------------------------------------------------------

        // 计算点在平面上的参数值
        public static Pnt2d Parameters(this Pln pln, Pnt pnt)
        {
            double u = 0, v = 0;
            ElSLib.Parameters(pln, pnt, ref u, ref v);
            return new Pnt2d(u, v);
        }

        //--------------------------------------------------------------------------------------------------
        
        // 计算平面上参数对应的点坐标
        public static Pnt Value(this Pln pln, Pnt2d uv)
        {
            return ElSLib.Value(uv.X, uv.Y, pln);
        }

        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Quaternion

        // 将四元数转换为轴-角表示
        public static Ax3 ToAx3(this Quaternion rotation, Pnt location)
        {
            return new Ax3(location,
                rotation.Multiply(new Vec(0, 0, 1)).ToDir(),
                rotation.Multiply(new Vec(1, 0, 0)).ToDir());
        }

        //--------------------------------------------------------------------------------------------------

        // 将四元数转换为欧拉角表示
        public static (double yaw, double pitch, double roll) ToEuler(this Quaternion rotation)
        {
            double y = 0, p = 0, r = 0;
            rotation.GetEulerAngles(ref y, ref p, ref r);
            return (y, p, r);
        }

        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Ax22d

        // 计算二维轴的方向
        public static int Sense(this Ax22d ax)
        {
            return ax.YAxis.Angle(ax.XAxis) > 0 ? 1 : -1;
        }

        //--------------------------------------------------------------------------------------------------

        #endregion
    }
}

这段代码定义了针对 PntPnt2dVec2dDirPlnQuaternionAx22d 类型的扩展方法,提供了各种便捷的数学计算和操作功能。

cs 复制代码
using System;
using System.Collections.Generic;
using Macad.Occt;

namespace Macad.Core
{
    public static class ListExtensions
    {
        /// <summary>
        /// 检查 IEnumerable 中是否包含与给定的 TopoDS_Shape 相同的形状对象。
        /// </summary>
        public static bool ContainsSame<T>(this IEnumerable<T> list, TopoDS_Shape shape) where T : TopoDS_Shape
        {
            if (shape == null)
            {
                // 如果给定的形状为空,检查集合中是否存在空形状对象
                foreach (var item in list)
                {
                    if (item == null)
                        return true;
                }
                return false;
            }
            else
            {
                // 否则,检查集合中是否存在与给定形状相同的形状对象
                foreach (var item in list)
                {
                    if (shape.IsSame(item))
                        return true;
                }
                return false;
            }
        }

        /// <summary>
        /// 检查 IEnumerable 中是否包含与给定的 TopoDS_Shape 相似(partner)的形状对象。
        /// </summary>
        public static bool ContainsPartner<T>(this IEnumerable<T> list, TopoDS_Shape shape) where T : TopoDS_Shape
        {
            if (shape == null)
            {
                // 如果给定的形状为空,检查集合中是否存在空形状对象
                foreach (var item in list)
                {
                    if (item == null)
                        return true;
                }
                return false;
            }
            else
            {
                // 否则,检查集合中是否存在与给定形状相似(partner)的形状对象
                foreach (var item in list)
                {
                    if (shape.IsPartner(item))
                        return true;
                }
                return false;
            }
        }

        /// <summary>
        /// 返回 IList 中与给定的 TopoDS_Shape 相同的形状对象的索引。
        /// </summary>
        public static int IndexOfSame<T>(this IList<T> list, TopoDS_Shape item) where T : TopoDS_Shape
        {
            int size = list.Count;
            if (item == null)
            {
                // 如果给定的形状为空,返回列表中第一个空形状对象的索引
                for (int i = 0; i < size; i++)
                    if (list[i] == null)
                        return i;
                return -1;
            }
            else
            {
                // 否则,返回列表中与给定形状相同的形状对象的索引
                for (int i = 0; i < size; i++)
                {
                    if (item.IsSame(list[i]))
                        return i;
                }
                return -1;
            }
        }
    }
}

这段代码定义了针对 IEnumerable<T>IList<T> 接口的扩展方法,用于处理 TopoDS_Shape 类型的对象。

  • ContainsSame<T>(this IEnumerable<T> list, TopoDS_Shape shape):检查 IEnumerable 中是否包含与给定的 TopoDS_Shape 相同的形状对象。

  • ContainsPartner<T>(this IEnumerable<T> list, TopoDS_Shape shape):检查 IEnumerable 中是否包含与给定的 TopoDS_Shape 相似(partner)的形状对象。

  • IndexOfSame<T>(this IList<T> list, TopoDS_Shape item):返回 IList 中与给定的 TopoDS_Shape 相同的形状对象的索引。

这些方法允许在集合中查找相同或相似的形状对象,并提供了方便的方式来处理形状对象的列表。

cs 复制代码
using Macad.Occt;

namespace Macad.Core
{
    public static class TopLocExtension
    {
        /// <summary>
        /// 将 TopLoc_Location 对象转换为 Ax3 对象。
        /// </summary>
        public static Ax3 ToAx3(this TopLoc_Location rotation)
        {
            // 获取变换矩阵
            var trsf = rotation.Transformation();
            // 获取旋转部分并将其转换为 Ax3 对象,使用平移部分的 Pnt 作为位置点
            return trsf.GetRotation().ToAx3(trsf.TranslationPart().ToPnt());
        }
    }
}

这个扩展方法允许将 TopLoc_Location 对象转换为 Ax3 对象,从而简化了代码中的操作。

cs 复制代码
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Macad.Occt;

namespace Macad.Core
{
    public static class TopoDSShapeExtensions
    {
        /// <summary>
        /// 获取组合实体。
        /// </summary>
        public static List<TopoDS_CompSolid> CompSolids(this TopoDS_Shape shape, bool distinct = true)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取实体。
        /// </summary>
        public static List<TopoDS_Solid> Solids(this TopoDS_Shape shape, bool distinct = true)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取壳体。
        /// </summary>
        public static List<TopoDS_Shell> Shells(this TopoDS_Shape shape, bool distinct = true)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取面。
        /// </summary>
        public static List<TopoDS_Face> Faces(this TopoDS_Shape shape, bool distinct = true)
        {
            Debug.Assert(shape != null);
            
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取线框。
        /// </summary>
        public static List<TopoDS_Wire> Wires(this TopoDS_Shape shape, bool distinct = true)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取边。
        /// </summary>
        public static List<TopoDS_Edge> Edges(this TopoDS_Shape shape, bool distinct = true)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取顶点。
        /// </summary>
        public static List<TopoDS_Vertex> Vertices(this TopoDS_Shape shape, bool distinct = true)
        {
            // 省略内容
        }
        
        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取质心。
        /// </summary>
        public static Pnt CenterOfMass(this TopoDS_Shape shape)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取面积。
        /// </summary>
        public static double Area(this TopoDS_Shape shape)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取体积。
        /// </summary>
        public static double Volume(this TopoDS_Shape shape)
        {
            // 省略内容
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取包围盒。
        /// </summary>
        public static Bnd_Box BoundingBox(this TopoDS_Shape shape)
        {
            // 省略内容
        }
        
        //--------------------------------------------------------------------------------------------------

        // 省略其他扩展方法
    }
}

这些扩展方法用于检索给定形状(TopoDS_Shape)中包含的不同类型的子形状,例如组合实体、实体、壳体、面、线框、边和顶点。此外,还提供了一些计算几何特性的方法,如质心、面积、体积和包围盒。

cs 复制代码
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using Macad.Core.Topology;
using Macad.Occt;
using Macad.Occt.Helper;

namespace Macad.Core
{
    public sealed class MessageHandler : IDisposable
    {
        #region Properties

        /// <summary>
        /// 获取或设置消息项集合。
        /// </summary>
        public ObservableCollection<MessageItem> MessageItems
        {
            get { return _MessageItems; }
            set
            {
                if (_MessageItems != value)
                {
                    _MessageItems = value;
                    RaiseStaticPropertyChanged();
                }
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取当前引用实体。
        /// </summary>
        public Entity CurrentReferenceEntity
        {
            get { return _ProcessingEntitiesStack.Count > 0 ? _ProcessingEntitiesStack.Peek() : null; }
        }

        //--------------------------------------------------------------------------------------------------
        
        #endregion

        #region Member

        ObservableCollection<MessageItem> _MessageItems;
        ConditionalWeakTable<Entity, List<MessageItem>> _EntityMessages;

        readonly Stack<Entity> _ProcessingEntitiesStack = new Stack<Entity>();

        MessageRouter _MessageRouter;

        [ThreadStatic]
        static readonly bool _IsMainThread = true;

        //--------------------------------------------------------------------------------------------------

        #endregion        
            
        #region Initialization and Property handling

        /// <summary>
        /// 构造函数。
        /// </summary>
        public MessageHandler()
        {
            _MessageItems = new ObservableCollection<MessageItem>();
            _EntityMessages = new ConditionalWeakTable<Entity, List<MessageItem>>();
            
            _MessageRouter = new MessageRouter();
            _MessageRouter.MessageArrived += _MessageRouter_MessageArrived;
            _MessageRouter.TraceLevel = Message_Gravity.Warning;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 析构函数。
        /// </summary>
        ~MessageHandler()
        {
            Dispose(false);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 释放资源。
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        //--------------------------------------------------------------------------------------------------

        void Dispose(bool disposing)
        {
            _MessageRouter?.Dispose();
            _MessageRouter = null;
        }

        //--------------------------------------------------------------------------------------------------

        void _MessageRouter_MessageArrived(string text, Message_Gravity gravity)
        {
            if (!_IsMainThread)
            {
                // 只接受主线程之外的其他线程发送的消息
                return;
            }

            MessageSeverity severity;
            switch (gravity)
            {
                case Message_Gravity.Trace:
                    severity = MessageSeverity.Trace;
                    break;
                case Message_Gravity.Info:
                    severity = MessageSeverity.Info;
                    break;
                case Message_Gravity.Warning:
                case Message_Gravity.Alarm:
                    severity = MessageSeverity.Warning;
                    break;
                case Message_Gravity.Fail:
                    severity = MessageSeverity.Error;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(gravity), gravity, null);
            }

            AddMessage(new MessageItem(severity, text));
        }

        //--------------------------------------------------------------------------------------------------

        public event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;

        public void RaiseStaticPropertyChanged([CallerMemberName] String propertyName = "")
        {
            StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
        }

        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Messages

        public event EventHandler<MessageItem> MessageThrown;

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 添加消息项。
        /// </summary>
        public void AddMessage(MessageItem message)
        {
            if (message.Sender != null && message.Sender.TryGetTarget(out var entity))
            {
                var list = _EntityMessages.GetValue(entity, key => new List<MessageItem>());
                list.Add(message);
            }

            _MessageItems.Add(message);
            MessageThrown?.Invoke(message.Sender, message);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 清除指定实体的所有消息。
        /// </summary>
        public void ClearEntityMessages(Entity entity)
        {
            if (!_EntityMessages.TryGetValue(entity, out var messages))
            {
                return;
            }

            messages.ForEach(item => _MessageItems.Remove(item));
            _EntityMessages.Remove(entity);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 清除所有实体的消息。
        /// </summary>
        public void ClearAllEntityMessages()
        {
            foreach (var item in _MessageItems.ToArray())
            {
                if(item.Sender == null)
                    continue;

                _MessageItems.Remove(item);
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取指定实体的消息。
        /// </summary>
        public List<MessageItem> GetEntityMessages(Entity entity)
        {
            return _EntityMessages.TryGetValue(entity, out var list) ? list : null;
        }

        //--------------------------------------------------------------------------------------------------
        
        #endregion

        #region Processing

        /// <summary>
        /// 处理消息事件的原因。
        /// </summary>
        public enum ProgressMessageEventReason
        {
            ProcessingStarted,
            ProcessingStopped
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 处理消息事件参数。
        /// </summary>
        public class ProgressMessageEventArgs : EventArgs
        {
            public ProgressMessageEventReason Reason;
            public string Description;

            public ProgressMessageEventArgs(ProgressMessageEventReason reason, string description)
            {
                Reason = reason;
                Description = description;
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 进度消息事件。
        /// </summary>
        public event EventHandler<ProgressMessageEventArgs> ProgressMessage;

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 当处理开始时调用。
        /// </summary>
        public void OnProcessingStarted(Entity referenceEntity, string description)
        {
            _ProcessingEntitiesStack.Push(referenceEntity);

            if (_ProcessingEntitiesStack.Count == 1)
            {
                ProgressMessage?.Invoke(this, new ProgressMessageEventArgs(ProgressMessageEventReason.ProcessingStarted, description));
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 当处理结束时调用。
        /// </summary>
        public void OnProcessingStopped()
        {
            if (_ProcessingEntitiesStack.Count > 0)
            {
                _ProcessingEntitiesStack.Pop();
            }

            if (_ProcessingEntitiesStack.Count == 0)
            {
                ProgressMessage?.Invoke(this, new ProgressMessageEventArgs(ProgressMessageEventReason.ProcessingStopped, null));
            }
        }

        //--------------------------------------------------------------------------------------------------

        #endregion
    }
}

这是一个用于处理消息的类。它包含了一些属性和方法来管理消息集合、处理消息的事件等。其中包括了消息的添加、清除、获取等操作,以及处理过程的开始和结束。

cs 复制代码
using System;
using System.Text;
using Macad.Core.Topology;

namespace Macad.Core
{
    /// <summary>
    /// 消息严重性级别枚举。
    /// </summary>
    public enum MessageSeverity
    {
        Trace = 0,
        Info = 1,
        Warning = 2,
        Error = 3
    }

    //--------------------------------------------------------------------------------------------------
    //--------------------------------------------------------------------------------------------------

    /// <summary>
    /// 消息项类。
    /// </summary>
    public class MessageItem
    {
        /// <summary>
        /// 获取时间戳。
        /// </summary>
        public DateTime TimeStamp { get; }

        /// <summary>
        /// 获取消息文本。
        /// </summary>
        public string Text { get; }

        /// <summary>
        /// 获取消息严重性级别。
        /// </summary>
        public MessageSeverity Severity { get; }

        /// <summary>
        /// 获取消息解释。
        /// </summary>
        public string[] Explanation { get; }

        /// <summary>
        /// 获取消息发送者的弱引用。
        /// </summary>
        public WeakReference<Entity> Sender { get; }

        //--------------------------------------------------------------------------------------------------

        static readonly char[] _LineBreaks = { '\n', '\r' };

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 构造函数。
        /// </summary>
        public MessageItem(MessageSeverity severity, string text, string explanation=null, Entity sender=null)
        {
            TimeStamp = DateTime.Now;
            Severity = severity;
            Text = text;
            Sender = sender != null ? new WeakReference<Entity>(sender) : null;
            Explanation = explanation?.Split(_LineBreaks, StringSplitOptions.RemoveEmptyEntries);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 返回消息的字符串表示形式。
        /// </summary>
        public override string ToString()
        {
            var sb = new StringBuilder();
            switch (Severity)
            {
                case MessageSeverity.Trace:
                    sb.Append("Trace: ");
                    break;
                case MessageSeverity.Info:
                    sb.Append("Info: ");
                    break;
                case MessageSeverity.Warning:
                    sb.Append("Warning: ");
                    break;
                case MessageSeverity.Error:
                    sb.Append("Error: ");
                    break;
            }

            if (Sender != null && Sender.TryGetTarget(out var senderEntity))
            {
                sb.Append('[');
                sb.Append(senderEntity.Name);
                sb.Append("] ");
            }

            sb.AppendLine(Text);
            if (Explanation != null)
            {
                foreach (var s in Explanation)
                {
                    sb.AppendLine(s);
                }
            }

            return sb.ToString();
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 返回消息的简略字符串表示形式。
        /// </summary>
        public string ToShortString()
        {
            string result;
            switch (Severity)
            {
                case MessageSeverity.Trace:
                    result = "Trace: ";
                    break;
                case MessageSeverity.Info:
                    result = "Info: ";
                    break;
                case MessageSeverity.Warning:
                    result = "Warning: ";
                    break;
                case MessageSeverity.Error:
                    result = "Error: ";
                    break;
                default:
                    result = "";
                    break;
            }

            return result + Text;
        }

        //--------------------------------------------------------------------------------------------------

    }

}

这段代码定义了消息项类 MessageItem 和消息严重性级别枚举 MessageSeverity。消息项类包含了消息的时间戳、文本、严重性级别、解释和发送者的信息。

cs 复制代码
using System;
using System.Runtime.InteropServices;
using Macad.Core.Topology;
using Macad.Occt;

namespace Macad.Core
{
    /// <summary>
    /// 消息类,提供发送各种消息的静态方法。
    /// </summary>
    public static class Messages
    {
        /// <summary>
        /// 发送跟踪消息。
        /// </summary>
        public static void Trace(string line, string explanation = null, Entity sender = null)
        {
            var handler = CoreContext.Current?.MessageHandler;
            handler?.AddMessage(new MessageItem(MessageSeverity.Trace, line, explanation, sender ?? handler.CurrentReferenceEntity));
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 发送信息消息。
        /// </summary>
        public static void Info(string line, string explanation = null, Entity sender = null)
        {
            var handler = CoreContext.Current?.MessageHandler;
            handler?.AddMessage(new MessageItem(MessageSeverity.Info, line, explanation, sender ?? handler.CurrentReferenceEntity));
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 发送警告消息。
        /// </summary>
        public static void Warning(string line, string explanation = null, Entity sender = null)
        {
            var handler = CoreContext.Current?.MessageHandler;
            handler?.AddMessage(new MessageItem(MessageSeverity.Warning, line, explanation, sender ?? handler.CurrentReferenceEntity));
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 发送错误消息。
        /// </summary>
        public static void Error(string line, string explanation = null, Entity sender = null)
        {
            var handler = CoreContext.Current?.MessageHandler;
            handler?.AddMessage(new MessageItem(MessageSeverity.Error, line, explanation, sender ?? handler.CurrentReferenceEntity));
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 发送异常消息。
        /// </summary>
        public static void Exception(string line, Exception exception, Entity sender = null)
        {
            string msg;
            if (exception is SEHException)
            {
                // 尝试从本地获取信息
                var info = Interop.ExceptionHelper.GetNativeExceptionInfo(Marshal.GetExceptionPointers());
                if (info != null)
                {
                    msg = $"{info.Message} [{info.Source}]";
                }
                else
                {
                    msg = "未知本地异常。";
                }
            }
            else
            {
                msg = $"{exception.Message} [ {exception.GetType().FullName} ]";
            }

            string expl = "异常: " + msg + "\n" + exception.StackTrace;
            var handler = CoreContext.Current?.MessageHandler;
            handler?.AddMessage(new MessageItem(MessageSeverity.Error, line, expl, sender ?? handler.CurrentReferenceEntity));
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 发送报告消息。
        /// </summary>
        public static void Report(Message_Report report)
        {
            report.SendMessages(Message.DefaultMessenger());
        }

        //--------------------------------------------------------------------------------------------------

    }
}

这段代码定义了一个静态类 Messages,其中包含了用于发送各种消息的静态方法,如跟踪消息、信息消息、警告消息、错误消息、异常消息和报告消息。

cs 复制代码
using System;
using Macad.Core.Topology;

namespace Macad.Core
{
    /// <summary>
    /// 表示一个处理范围,用于在其范围内开始和结束处理。
    /// </summary>
    public sealed class ProcessingScope : IDisposable
    {
        /// <summary>
        /// 初始化处理范围并开始处理。
        /// </summary>
        /// <param name="referenceEntity">参考实体。</param>
        /// <param name="description">描述处理的信息。</param>
        public ProcessingScope(Entity referenceEntity, string description)
        {
            CoreContext.Current?.MessageHandler?.OnProcessingStarted(referenceEntity, description);
        }
        
        /// <summary>
        /// 处理范围的析构函数,结束处理。
        /// </summary>
        ~ProcessingScope()
        {
            Dispose();
        }

        /// <inheritdoc/>
        public void Dispose()
        {
            CoreContext.Current?.MessageHandler?.OnProcessingStopped();
            GC.SuppressFinalize(this);
        }
    }
}

这段代码定义了一个 ProcessingScope 类,它表示一个处理范围,用于在其范围内开始和结束处理。

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using Macad.Core.Geom;
using Macad.Core.Shapes;
using Macad.Common;
using Macad.Occt;

namespace Macad.Core
{
    #region 类 BRepTopologyTreeNodes

    /// <summary>
    /// 表示 BRepTopologyTreeNode 实例的集合。
    /// </summary>
    public class BRepTopologyTreeNodes : List<BRepTopologyTreeNode>
    {
        /// <summary>
        /// 使用指定的容量初始化 BRepTopologyTreeNodes 类的新实例。
        /// </summary>
        /// <param name="capacity">列表可以包含的初始元素数。</param>
        public BRepTopologyTreeNodes(int capacity) 
            : base(capacity)
        {
        }

        /// <summary>
        /// 初始化 BRepTopologyTreeNodes 类的新实例。
        /// </summary>
        public BRepTopologyTreeNodes()
        {
        }

        /// <summary>
        /// 初始化 BRepTopologyTreeNodes 类的新实例,并将元素添加到指定的集合。
        /// </summary>
        /// <param name="collection">要复制到新列表中的集合的元素。</param>
        public BRepTopologyTreeNodes(IEnumerable<BRepTopologyTreeNode> collection)
            : base(collection)
        {
        }
    }

    #endregion

    #region 类 BRepTopologyTreeProperty

    /// <summary>
    /// 表示 B-Rep 拓扑树节点的属性。
    /// </summary>
    public class BRepTopologyTreeProperty
    {
        /// <summary>
        /// 获取属性的类别。
        /// </summary>
        public string Category { get; }

        /// <summary>
        /// 获取属性的名称。
        /// </summary>
        public string Name { get; }

        /// <summary>
        /// 获取属性的值。
        /// </summary>
        public string Value { get; }

        /// <summary>
        /// 使用指定的类别、名称和值初始化 BRepTopologyTreeProperty 类的新实例。
        /// </summary>
        /// <param name="category">属性的类别。</param>
        /// <param name="name">属性的名称。</param>
        /// <param name="value">属性的值。</param>
        public BRepTopologyTreeProperty(string category, string name, string value)
        {
            Category = category;
            Name = name;
            Value = value;
        }
    }

    #endregion

    #region 类 BRepTopologyTreeNode

    /// <summary>
    /// 表示 B-Rep 拓扑树中的节点。
    /// </summary>
    public class BRepTopologyTreeNode
    {
        #region 属性

        /// <summary>
        /// 获取节点的名称。
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// 获取节点的子节点。
        /// </summary>
        public BRepTopologyTreeNodes Children
        {
            get
            {
                // 如果子节点尚未初始化,则初始化它们
                if (_Children is null)
                    _InitChildren();
                return _Children;
            }
        }

        /// <summary>
        /// 获取与节点关联的 B-Rep 形状。
        /// </summary>
        public TopoDS_Shape BrepShape { get; }

        /// <summary>
        /// 获取与节点关联的属性。
        /// </summary>
        public List<BRepTopologyTreeProperty> Properties
        {
            get
            {
                // 如果属性尚未初始化,则初始化它们
                if (_Properties is null)
                    _InitProperties();
                return _Properties;
            }
        }

        /// <summary>
        /// 获取与节点关联的形状。
        /// </summary>
        public Shape Shape { get; }

        #endregion

        #region 构造函数和设置器

        /// <summary>
        /// 使用指定的形状初始化 BRepTopologyTreeNode 类的新实例。
        /// </summary>
        /// <param name="shape">与节点关联的形状。</param>
        public BRepTopologyTreeNode(Shape shape)
        {
            Shape = shape;
            BrepShape = shape?.GetBRep();
            _TopLevelShape = BrepShape;

            Name = "无效形状";
            if (BrepShape != null)
            {
                Name = _GetShapeTypeName(BrepShape.ShapeType());
            }
        }

        /// <summary>
        /// 使用指定的 B-Rep 形状初始化 BRepTopologyTreeNode 类的新实例。
        /// </summary>
        /// <param name="brepShape">与节点关联的 B-Rep 形状。</param>
        /// <param name="topLevelShape">与节点关联的顶级形状。</param>
        /// <param name="shape">与节点关联的形状。</param>
        public BRepTopologyTreeNode(TopoDS_Shape brepShape, TopoDS_Shape topLevelShape = null, Shape shape = null)
        {
            Shape = shape;
            BrepShape = brepShape;
            _TopLevelShape = topLevelShape ?? brepShape;

            Name = "无效形状";
            if (brepShape != null)
            {
                Name = _GetShapeTypeName(brepShape.ShapeType());
            }
        }

        /// <summary>
        /// 使用指定的名称、包含的节点和顶级形状初始化 BRepTopologyTreeNode 类的新实例。
        /// </summary>
        /// <param name="name">节点的名称。</param>
        /// <param name="containedNodes">此节点中包含的子节点。</param>
        /// <param name="topLevelShape">与节点关联的顶级形状。</param>
        /// <param name="shape">与节点关联的形状。</param>
        public BRepTopologyTreeNode(string name, BRepTopologyTreeNodes containedNodes, TopoDS_Shape topLevelShape, Shape shape = null)
        {
            Shape = shape;
            Name = $"{name} ({containedNodes.Count})";
            _Children = containedNodes;
            _TopLevelShape = topLevelShape;
            _Properties = new List<BRepTopologyTreeProperty>();
        }

        /// <summary>
        /// 向节点名称添加索引前缀。
        /// </summary>
        /// <param name="i">要添加为前缀的索引。</param>
        public void AddIndexPrefix(int i)
        {
            Name = $"[{i}] {Name}";
        }

        #endregion

        #region 成员

        BRepTopologyTreeNodes _Children;
        List<BRepTopologyTreeProperty> _Properties;
        readonly TopoDS_Shape _TopLevelShape;

        #endregion

        #region 子节点

        /// <summary>
        /// 初始化子节点。
        /// </summary>
        void _InitChildren()
        {
            if (_Children != null)
                return;

            if (BrepShape == null)
                return;

            _Children = new BRepTopologyTreeNodes();

            switch (BrepShape.ShapeType())
            {
                case TopAbs_ShapeEnum.COMPOUND:
                    _AddShapeCollection(BrepShape.Solids(), true);
                    _AddShapeCollection(BrepShape.Shells(), true);
                    _AddShapeCollection(BrepShape.Faces(), true);
                    _AddShapeCollection(BrepShape.Wires(), true);
                    _AddShapeCollection(BrepShape.Edges(), true);
                    _AddShapeCollection(BrepShape.Vertices(), true);
                    break;

                case TopAbs_ShapeEnum.COMPSOLID:
                    _AddShapeCollection(BrepShape.Solids());
                    break;

                case TopAbs_ShapeEnum.SOLID:
                    _AddShapeCollection(BrepShape.Shells(), true);
                    _AddShapeCollection(BrepShape.Faces(), true);
                    _AddShapeCollection(BrepShape.Wires(), true);
                    _AddShapeCollection(BrepShape.Edges(), true);
                    _AddShapeCollection(BrepShape.Vertices(), true);
                    break;

                case TopAbs_ShapeEnum.SHELL:
                    _AddShapeCollection(BrepShape.Faces(), true);
                    _AddShapeCollection(BrepShape.Wires(), true);
                    _AddShapeCollection(BrepShape.Edges(), true);
                    _AddShapeCollection(BrepShape.Vertices(), true);
                    break;

                case TopAbs_ShapeEnum.FACE:
                    _AddShapeCollection(BrepShape.Wires());
                    break;

                case TopAbs_ShapeEnum.WIRE:
                    _AddShapeCollection(BrepShape.Edges());
                    break;

                case TopAbs_ShapeEnum.EDGE:
                    _AddShapeCollection(BrepShape.Vertices());
                    break;
            }
        }

        #endregion

        #region 属性列表

        /// <summary>
        /// 初始化属性列表。
        /// </summary>
        void _InitProperties()
        {
            if (_Properties != null)
                return;

            _Properties = new List<BRepTopologyTreeProperty>();
            try
            {
                _AddDefaultProperties();

                switch (BrepShape.ShapeType())
                {
                    case TopAbs_ShapeEnum.SHELL:
                        _AddShellProperties(BrepShape as TopoDS_Shell ?? TopoDS.Shell(BrepShape));
                        break;

                    case TopAbs_ShapeEnum.FACE:
                        _AddFaceProperties(BrepShape as TopoDS_Face ?? TopoDS.Face(BrepShape));
                        break;

                    case TopAbs_ShapeEnum.WIRE:
                        _AddWireProperties(BrepShape as TopoDS_Wire ?? TopoDS.Wire(BrepShape));
                        break;

                    case TopAbs_ShapeEnum.EDGE:
                        _AddEdgeProperties(BrepShape as TopoDS_Edge ?? TopoDS.Edge(BrepShape));
                        break;

                    case TopAbs_ShapeEnum.VERTEX:
                        _AddVertexProperties(BrepShape as TopoDS_Vertex ?? TopoDS.Vertex(BrepShape));
                        break;
                }
            }
            catch (Exception e)
            {
                Messages.Exception($"获取 B-Rep 形状 {Name} 的属性时出错", e);
            }
        }

        #endregion

        #region 集合

        /// <summary>
        /// 将形状集合添加到子节点。
        /// </summary>
        /// <typeparam name="T">形状的类型。</typeparam>
        /// <param name="shapeList">要添加到子节点的形状集合。</param>
        /// <param name="group">指示是否对集合进行分组。</param>
        void _AddShapeCollection<T>(IReadOnlyList<T> shapeList, bool group = false) where T: TopoDS_Shape
        {
            if (!shapeList.Any())
                return;

            var collection = group ? new BRepTopologyTreeNodes() : _Children;
            for (var i = 0; i < shapeList.Count; i++)
            {
                var treeNode = new BRepTopologyTreeNode(shapeList[i], _TopLevelShape, Shape);
                treeNode.AddIndexPrefix(i);
                collection.Add(treeNode);
            }

            if (group)
            {
                var name = typeof(T).Name.TrimPrefixes("TopoDS_") + "s";
                if (name == "Vertexs")
                    name = "Vertices";

                _Children.Add(new BRepTopologyTreeNode(name, collection, _TopLevelShape, Shape));
            }
        }

        #endregion
    }

    #endregion
}

这段代码定义了一个名为BRepTopologyTreeNode的类,用于表示 B-Rep(Boundary Representation)拓扑树中的节点。B-Rep是一种用于描述实体几何形状的方法,它通过表示实体的边界以及边界之间的关系来描述几何形状。

该类的主要作用包括:

  1. 表示B-Rep拓扑树中的节点,每个节点代表一个几何形状元素(如面、边、点等)或由多个子节点组成的组合形状。
  2. 可以获取节点的名称、子节点、B-Rep形状、属性等信息。
  3. 提供了初始化子节点和属性列表的方法,以便在需要时进行延迟加载。
  4. 可以添加索引前缀以标识节点在其父节点中的位置。
  5. 可以根据B-Rep形状的类型添加相应类型的子节点集合,并可选择对子节点进行分组。
  6. 提供了获取B-Rep形状的类型名称、初始化属性列表、初始化子节点、添加子节点等功能的私有辅助方法。

总之,该类用于构建和管理B-Rep拓扑树的节点,使用户能够方便地表示和操作复杂的几何形状结构。

cs 复制代码
using Macad.Common;
using Macad.Occt;

namespace Macad.Core
{
    public static class OcctColorUtils
    {
        public static Color Color(Quantity_Color color)
        {
            var red = (float)color.Red();
            var green = (float)color.Green();
            var blue = (float)color.Blue();
            return new Color(red, green, blue);
        }

        //--------------------------------------------------------------------------------------------------

        public static Color Color(Quantity_NameOfColor colorName)
        {
            return Color(new Quantity_Color(colorName));
        }

        //--------------------------------------------------------------------------------------------------

        public static Quantity_Color ToColor(this Quantity_NameOfColor colorName)
        {
            return new Quantity_Color(colorName);
        }

        //--------------------------------------------------------------------------------------------------

        public static Graphic3d_MaterialAspect ToAspect(this Graphic3d_NameOfMaterial materialName)
        {
            return new Graphic3d_MaterialAspect(materialName);
        }

        //--------------------------------------------------------------------------------------------------

        public static Quantity_Color ToQuantityColor(this Color color)
        {
            return new Quantity_Color(color.Red, color.Green, color.Blue, Quantity_TypeOfColor.sRGB);
        }

        //--------------------------------------------------------------------------------------------------
    }
}

整段代码的作用是提供了一些用于颜色和材质转换的工具方法。这些方法包括:

  1. Quantity_Color类型的颜色转换为Color类型的颜色。

  2. Quantity_NameOfColor类型的颜色名称转换为Color类型的颜色。

  3. Quantity_NameOfColor类型的颜色名称转换为Quantity_Color类型的颜色。

  4. Graphic3d_NameOfMaterial类型的材质名称转换为Graphic3d_MaterialAspect类型的材质属性。

  5. Color类型的颜色转换为Quantity_Color类型的颜色。

cs 复制代码
using System.Diagnostics; // 导入System.Diagnostics命名空间,提供Debug类的定义
using Macad.Common.Serialization; // 导入Macad.Common.Serialization命名空间,提供序列化相关的类和接口
using Macad.Occt; // 导入Macad.Occt命名空间,提供OpenCASCADE的类型定义

namespace Macad.Core
{
    public class OcctSerializers // 定义名为OcctSerializers的类,用于提供OpenCASCADE类型的序列化器
    {
        class OcctSerializer_Pnt2d : ISerializer // 定义内部类OcctSerializer_Pnt2d,实现ISerializer接口,用于Pnt2d类型的序列化
        {
            public bool Write(Writer writer, object obj, SerializationContext context)
            {
                var pnt2d = (Pnt2d)obj; // 将传入的对象转换为Pnt2d类型
                double[] values = { pnt2d.X, pnt2d.Y }; // 提取Pnt2d对象的坐标值
                return _DoubleArraySerializer.Write(writer, values, context); // 调用_DoubleArraySerializer对坐标值进行序列化
            }

            public object Read(Reader reader, object obj, SerializationContext context)
            {
                var values = (double[])_DoubleArraySerializer.Read(reader, null, context); // 从Reader中读取序列化的坐标值
                if ((values != null) && (values.Length == 2)) // 验证读取的坐标值是否正确
                {
                    return new Pnt2d(values[0], values[1]); // 创建新的Pnt2d对象并返回
                }
                return null; // 如果读取的坐标值不正确,则返回null
            }
        }

        //--------------------------------------------------------------------------------------------------

        // 类似地定义其他OpenCASCADE类型的序列化器,包括Pnt、Vec、Dir、Quaternion、Ax1、Ax2和Pln
        // 每个序列化器都包括Write和Read方法,用于将对象序列化为数组或从数组中反序列化为对象

        //--------------------------------------------------------------------------------------------------

        static bool _IsInitialized; // 静态变量,标识是否已初始化
        static ISerializer _DoubleArraySerializer; // 静态变量,用于序列化double数组

        public static void Init() // 初始化方法,用于注册OpenCASCADE类型的序列化器
        {
            Debug.Assert(!_IsInitialized); // 确保尚未初始化

            _DoubleArraySerializer = Serializer.GetSerializer(typeof(double[])); // 获取序列化double数组的序列化器

            // 注册各个OpenCASCADE类型的序列化器
            Serializer.AddSerializer(typeof(Pnt), new OcctSerializer_Pnt());
            Serializer.AddSerializer(typeof(Pnt2d), new OcctSerializer_Pnt2d());
            Serializer.AddSerializer(typeof(Vec), new OcctSerializer_Vec());
            Serializer.AddSerializer(typeof(Dir), new OcctSerializer_Dir());
            Serializer.AddSerializer(typeof(Quaternion), new OcctSerializer_Quaternion());
            Serializer.AddSerializer(typeof(Ax1), new OcctSerializer_Ax1());
            Serializer.AddSerializer(typeof(Ax2), new OcctSerializer_Ax2());
            Serializer.AddSerializer(typeof(Pln), new OcctSerializer_Pln());

            _IsInitialized = true; // 设置初始化标志为true
        }

        //--------------------------------------------------------------------------------------------------

    }
}

这段代码定义了一系列用于OpenCASCADE类型的序列化器,包括PntPnt2dVecDirQuaternionAx1Ax2Pln。每个序列化器都实现了ISerializer接口,包括Write方法用于将对象序列化为数组,以及Read方法用于从数组中反序列化为对象。在初始化方法Init中,这些序列化器被注册到了全局的Serializer对象中,以便在序列化和反序列化过程中使用。

cs 复制代码
using System; // 导入System命名空间,提供基本的系统类型和功能
using Macad.Common.Serialization; // 导入Macad.Common.Serialization命名空间,提供序列化相关的类和接口
using Macad.Occt; // 导入Macad.Occt命名空间,提供OpenCASCADE的类型定义

namespace Macad.Core
{
    [SerializeType] // 标记该枚举类型可被序列化
    public enum SubshapeType // 定义名为SubshapeType的枚举类型,用于表示子形状的类型
    {
        Vertex, // 顶点
        Edge, // 边
        Wire, // 线
        Face // 面
    }

    //--------------------------------------------------------------------------------------------------

    [SerializeType] // 标记该枚举类型可被序列化
    [Flags] // 标记该枚举为位标志枚举
    public enum SubshapeTypes // 定义名为SubshapeTypes的枚举类型,用于表示多个子形状的组合类型
    {
        None = 0, // 无
        Vertex = 1 << 0, // 顶点
        Edge = 1 << 1, // 边
        Wire = 1 << 2, // 线
        Face = 1 << 4, // 面
        All = Vertex | Edge | Wire | Face // 全部类型的组合
    }

    //--------------------------------------------------------------------------------------------------

    public static class SubshapeTypeHelper // 定义名为SubshapeTypeHelper的静态类,包含一些用于子形状类型操作的方法
    {
        public static SubshapeTypes GetTypes(SubshapeType topAbsShapeEnum) // 返回给定SubshapeType枚举值对应的SubshapeTypes位标志值
        {
            switch (topAbsShapeEnum)
            {
                case SubshapeType.Face:
                    return SubshapeTypes.Face;

                case SubshapeType.Edge:
                    return SubshapeTypes.Edge;

                case SubshapeType.Wire:
                    return SubshapeTypes.Wire;

                case SubshapeType.Vertex:
                    return SubshapeTypes.Vertex;

                default:
                    throw new NotImplementedException(); // 抛出未实现异常
            }
        }

        //--------------------------------------------------------------------------------------------------

        // 类似地定义其他用于获取子形状类型的方法,包括GetType和GetTypes方法

        //--------------------------------------------------------------------------------------------------

        public static TopAbs_ShapeEnum ToTopAbs(this SubshapeType type) // 将SubshapeType枚举值转换为TopAbs_ShapeEnum值
        {
            switch (type)
            {
                case SubshapeType.Vertex:
                    return TopAbs_ShapeEnum.VERTEX;
                case SubshapeType.Edge:
                    return TopAbs_ShapeEnum.EDGE;
                case SubshapeType.Wire:
                    return TopAbs_ShapeEnum.WIRE;
                case SubshapeType.Face:
                    return TopAbs_ShapeEnum.FACE;
                default:
                    throw new NotImplementedException(); // 抛出未实现异常
            }
        }

        //--------------------------------------------------------------------------------------------------

        // 类似地定义其他用于转换子形状类型的方法,包括ToAisSelectionMode和Count方法

        //--------------------------------------------------------------------------------------------------

    }
}

这段代码定义了用于处理子形状类型的枚举和辅助方法。首先定义了SubshapeType枚举,表示子形状的具体类型,包括顶点、边、线和面。然后定义了SubshapeTypes枚举,表示多个子形状类型的组合,使用了位标志枚举的特性。接着定义了SubshapeTypeHelper静态类,包含了一系列用于处理子形状类型的方法,包括获取子形状类型、转换类型以及计数等。这些方法可以方便地处理子形状类型的操作和转换。

cs 复制代码
using System; // 导入System命名空间,提供基本的系统类型和功能
using System.Collections.Generic; // 导入System.Collections.Generic命名空间,提供泛型集合类型的支持
using Macad.Occt; // 导入Macad.Occt命名空间,提供OpenCASCADE的类型定义

namespace Macad.Core // 声明名为Macad.Core的命名空间,该类位于Macad.Core命名空间下
{
    public class TopoDSShapeComparer : IEqualityComparer<TopoDS_Shape> // 定义名为TopoDSShapeComparer的类,实现了IEqualityComparer<TopoDS_Shape>接口,用于比较TopoDS_Shape对象
    {
        public enum CompareMode // 定义名为CompareMode的枚举类型,表示比较模式
        {
            Equal, // 相等
            Same, // 相同
            Partner // 伙伴
        }

        //--------------------------------------------------------------------------------------------------

        CompareMode _Mode = CompareMode.Equal; // 声明名为_Mode的字段,用于存储比较模式,默认为Equal

        //--------------------------------------------------------------------------------------------------

        public TopoDSShapeComparer(CompareMode mode) // 定义TopoDSShapeComparer类的构造函数,初始化比较模式
        {
            _Mode = mode; // 将传入的比较模式赋值给_Mode字段
        }

        //--------------------------------------------------------------------------------------------------

        public bool Equals(TopoDS_Shape x, TopoDS_Shape y) // 实现IEqualityComparer<TopoDS_Shape>接口中的Equals方法,用于比较两个TopoDS_Shape对象是否相等
        {
            if (x == null) // 如果第一个对象为null
                return y == null; // 则当第二个对象也为null时返回true,否则返回false

            return _Mode switch // 根据比较模式进行比较
            {
                CompareMode.Equal => x.Equals(y), // 如果比较模式为Equal,则调用Equals方法比较对象是否相等
                CompareMode.Same => x.IsSame(y), // 如果比较模式为Same,则调用IsSame方法比较对象是否相同
                CompareMode.Partner => x.IsPartner(y), // 如果比较模式为Partner,则调用IsPartner方法比较对象是否为伙伴
                _ => false // 默认情况下返回false
            };
        }

        //--------------------------------------------------------------------------------------------------

        public int GetHashCode(TopoDS_Shape obj) // 实现IEqualityComparer<TopoDS_Shape>接口中的GetHashCode方法,用于获取对象的哈希码
        {
            return _Mode switch // 根据比较模式获取哈希码
            {
                CompareMode.Equal => obj.GetHashCode(), // 如果比较模式为Equal,则直接返回对象的哈希码
                CompareMode.Same => obj.TShape().GetHashCode() ^ obj.Location().GetHashCode(), // 如果比较模式为Same,则获取对象的TShape和Location的哈希码,并进行异或操作
                CompareMode.Partner => obj.TShape().GetHashCode(), // 如果比较模式为Partner,则只获取对象的TShape的哈希码
                _ => 0 // 默认情况下返回0
            };
        }
    }
}

这段代码定义了一个名为TopoDSShapeComparer的类,用于比较TopoDS_Shape对象。它实现了IEqualityComparer<T>接口,其中TTopoDS_Shape类型。类中包含了一个枚举CompareMode,表示比较模式,包括相等、相同和伙伴。类中包含了一个字段 _Mode,用于存储比较模式,默认为相等。类中包含了构造函数用于初始化比较模式,以及Equals方法用于比较两个对象是否相等,GetHashCode方法用于获取对象的哈希码。

cs 复制代码
using System; // 导入System命名空间,提供基本的系统类型和功能
using System.Collections.Generic; // 导入System.Collections.Generic命名空间,提供泛型集合类型的支持
using System.IO; // 导入System.IO命名空间,提供文件和流的操作功能

namespace Macad.Core // 声明名为Macad.Core的命名空间,该类位于Macad.Core命名空间下
{
    internal static class ScriptCache // 声明名为ScriptCache的静态类,用于脚本缓存管理
    {
        struct CachedScriptFileInfo // 定义名为CachedScriptFileInfo的结构体,用于存储缓存的脚本文件信息
        {
            internal string FileName; // 存储文件名
            internal long Length; // 存储文件长度
            internal DateTime LastWriteTime; // 存储文件最后写入时间
        }

        class CachedScript // 定义名为CachedScript的内部类,用于存储缓存的脚本实例及其文件信息
        {
            internal ScriptInstance Instance; // 存储脚本实例
            internal CachedScriptFileInfo[] FileInfos; // 存储脚本文件信息数组
        }

        //--------------------------------------------------------------------------------------------------

        static readonly Dictionary<string, CachedScript> _CachedScripts = new Dictionary<string, CachedScript>(); // 声明名为_CachedScripts的静态只读字典,用于存储缓存的脚本信息

        //--------------------------------------------------------------------------------------------------

        internal static void Add(ScriptInstance scriptInstance, List<string> fileList) // 声明名为Add的静态方法,用于向脚本缓存中添加脚本实例及其文件信息
        {
            var entry = new CachedScript // 创建CachedScript实例
            {
                Instance = scriptInstance, // 设置脚本实例
                FileInfos = new CachedScriptFileInfo[fileList.Count], // 创建缓存的脚本文件信息数组
            };

            for (var i = 0; i < fileList.Count; i++) // 遍历文件列表
            {
                var fi = new FileInfo(fileList[i]); // 获取文件信息
                var info = new CachedScriptFileInfo() // 创建CachedScriptFileInfo实例
                {
                    FileName = fileList[i], // 设置文件名
                    Length = fi.Length, // 设置文件长度
                    LastWriteTime = fi.LastWriteTimeUtc // 设置文件最后写入时间
                };
                entry.FileInfos[i] = info; // 将文件信息添加到缓存的文件信息数组中
            }

            _CachedScripts[scriptInstance.Path] = entry; // 将脚本实例路径及其对应的缓存信息添加到缓存字典中
        }

        //--------------------------------------------------------------------------------------------------

        internal static ScriptInstance Find(string filename) // 声明名为Find的静态方法,用于在缓存中查找指定路径的脚本实例
        {
            try
            {
                filename = Path.GetFullPath(filename); // 获取指定路径的绝对路径
                if (!_CachedScripts.TryGetValue(filename, out CachedScript entry)) // 判断缓存字典中是否包含指定路径的缓存信息
                    return null; // 如果不包含,则返回null

                foreach (var info in entry.FileInfos) // 遍历缓存的文件信息数组
                {
                    var fi = new FileInfo(info.FileName); // 获取文件信息
                    if (fi.Length != info.Length // 检查文件长度和最后写入时间是否匹配
                        || fi.LastWriteTimeUtc != info.LastWriteTime)
                    {
                        // Cache miss,如果不匹配,则返回null
                        return null;
                    }
                }

                // Cache hit,如果匹配,则返回对应的脚本实例
                return entry.Instance;
            }
            catch (Exception)
            {
                return null; // 发生异常时返回null
            }
        }

        //--------------------------------------------------------------------------------------------------

        internal static void Clear() // 声明名为Clear的静态方法,用于清空缓存字典
        {
            _CachedScripts.Clear(); // 清空缓存字典
        }

        //--------------------------------------------------------------------------------------------------

    }
}

这段代码定义了一个名为 ScriptCache 的静态类,用于管理脚本的缓存。它包含了一个内部结构 CachedScriptFileInfo,用于存储缓存的脚本文件信息,以及一个内部类 CachedScript,用于存储缓存的脚本实例及其文件信息。类中声明了一个静态只读字典 _CachedScripts,用于存储缓存的脚本信息。还包含了方法 Add 用于向缓存中添加脚本实例及其文件信息,方法 Find 用于在缓存中查找指定路径的脚本实例,以及方法 Clear 用于清空缓存字典。

cs 复制代码
using System; // 导入 System 命名空间,提供基本的系统类型和功能
using System.Collections.Generic; // 导入 System.Collections.Generic 命名空间,提供泛型集合类型的支持
using System.Collections.Immutable; // 导入 System.Collections.Immutable 命名空间,提供不可变集合类型的支持
using System.Globalization; // 导入 System.Globalization 命名空间,提供区域性特定信息的功能
using System.IO; // 导入 System.IO 命名空间,提供文件和流的操作功能
using System.Linq; // 导入 System.Linq 命名空间,提供 LINQ 查询功能
using System.Reflection; // 导入 System.Reflection 命名空间,提供反射操作的功能
using System.Runtime.Loader; // 导入 System.Runtime.Loader 命名空间,提供程序集加载器的功能
using System.Text; // 导入 System.Text 命名空间,提供字符串处理功能
using Microsoft.CodeAnalysis; // 导入 Microsoft.CodeAnalysis 命名空间,提供编译代码分析的功能
using Microsoft.CodeAnalysis.CSharp; // 导入 Microsoft.CodeAnalysis.CSharp 命名空间,提供 C# 编译器的功能
using Microsoft.CodeAnalysis.CSharp.Scripting; // 导入 Microsoft.CodeAnalysis.CSharp.Scripting 命名空间,提供 C# 脚本编译的功能
using Microsoft.CodeAnalysis.Scripting; // 导入 Microsoft.CodeAnalysis.Scripting 命名空间,提供脚本编译的功能
using Macad.Common; // 导入 Macad.Common 命名空间,提供通用功能
using Macad.Occt; // 导入 Macad.Occt 命名空间,提供 OCC 相关功能
using Microsoft.CodeAnalysis.Scripting.Hosting; // 导入 Microsoft.CodeAnalysis.Scripting.Hosting 命名空间,提供脚本主机环境的功能

namespace Macad.Core // 声明名为 Macad.Core 的命名空间,该类位于 Macad.Core 命名空间下
{
    internal sealed class ScriptCompiler // 声明名为 ScriptCompiler 的密封类,用于编译脚本
    {
        static readonly string[] _DefaultReferenceAppAssemblies = // 默认的引用应用程序集
        {
            "Macad.Occt.dll",
            "Macad.Managed.dll",
            "Macad.Common.dll",
            "Macad.Core.dll",
            "Macad.Presentation.dll",
            "Macad.Interaction.dll",
            "Macad.Resources.dll"
        };

        static readonly string[] _DefaultImports = // 默认的导入命名空间
        {
            "System",
            "Macad.Common",
            "Macad.Core",
            "Macad.Core.Shapes",
            "Macad.Core.Toolkits",
            "Macad.Core.Topology",
            "Macad.Occt",
        };

        static readonly string[] _MetadataSearchPaths = // 元数据搜索路径
        {
            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
            Path.GetDirectoryName(typeof(object).Assembly.Location)
        };

        //--------------------------------------------------------------------------------------------------

        static float _Version; // 版本号

        //--------------------------------------------------------------------------------------------------

        static ScriptCompiler() // 静态构造函数
        {
            var version = Assembly.GetExecutingAssembly().GetName().Version; // 获取当前程序集的版本
            _Version = float.Parse($"{version.Major}.{version.Minor}", CultureInfo.InvariantCulture); // 解析版本号
        }

        //--------------------------------------------------------------------------------------------------

        internal static void OverrideVersion(float version) // 重写版本号
        {
            _Version = version; // 设置版本号
        }

        //--------------------------------------------------------------------------------------------------

        internal static bool Compile(ScriptInstance scriptInstance) // 编译脚本
        {
            var compiler = new ScriptCompiler(scriptInstance); // 创建 ScriptCompiler 实例
            if (!compiler._Compile()) // 调用 _Compile 方法进行编译
                return false; // 如果编译失败,返回 false

            ScriptCache.Add(scriptInstance, compiler._FileList); // 将编译结果添加到脚本缓存中

            return true; // 返回编译成功
        }

        //--------------------------------------------------------------------------------------------------

        readonly ScriptInstance _ScriptInstance; // 脚本实例
        readonly List<string> _FileList = new List<string>(); // 文件列表
        readonly bool _EnableDebugging = true; // 是否启用调试

        //--------------------------------------------------------------------------------------------------

        ScriptCompiler(ScriptInstance scriptInstance) // ScriptCompiler 构造函数
        {
            _ScriptInstance = scriptInstance; // 设置脚本实例
        }

        //--------------------------------------------------------------------------------------------------

        bool _Compile() // 编译
        {
            try
            {
                var codeStream = _ReadAndPreprocessFile(_ScriptInstance.Path); // 读取并预处理文件
                if (codeStream == null)
                    return false;

                var baseDirectory = Path.GetDirectoryName(_ScriptInstance.Path); // 获取基目录

                var metadataResolver = ScriptMetadataResolver.Default // 元数据解析器
                                                             .WithSearchPaths(_MetadataSearchPaths)
                                                             .WithBaseDirectory(baseDirectory);

                var sourceResolver = new ScriptSourceResolver(baseDirectory, _ReadAndPreprocessFile); // 源代码解析器

                var options = ScriptOptions.Default // 脚本选项
                                           .WithWarningLevel(4)
                                           .WithReferences(_DefaultReferenceAppAssemblies)
                                           .WithMetadataResolver(metadataResolver)
                                           .WithSourceResolver(sourceResolver)
                                           .WithImports(_DefaultImports)
                                           .WithEmitDebugInformation(_EnableDebugging)
                                           .WithLanguageVersion(LanguageVersion.Latest);

                var assemblyLoader = new InteractiveAssemblyLoader(); // 装载程序集
                foreach (var defaultAssembly in AssemblyLoadContext.Default.Assemblies) // 加载默认程序集
                {
                    assemblyLoader.RegisterDependency(defaultAssembly);
                }
                // This extra reference is needed for unit test runner which isolate this assembly
                assemblyLoader.RegisterDependency(Assembly.GetExecutingAssembly()); // 注册依赖程序集

                var script = CSharpScript.Create(codeStream, options, _ScriptInstance.ContextInstance.GetType(), assemblyLoader); // 创建脚本
                var results = script.Compile(); // 编译脚本
                codeStream.Dispose(); // 释放流资源

                var hasErrors = _ReportResults(results); // 检查编译结果
                if (!hasErrors) // 如果没有错误
                {
                    return _ScriptInstance.Init(script.CreateDelegate()); // 初始化脚本实例
                }
            }
            catch (Exception e) // 捕获异常
            {
                Messages.Exception("Script compilation failed. Scriptfile: " + _ScriptInstance.Path, e); // 报告异常
            }
            return false; // 返回编译失败
        }

        //--------------------------------------------------------------------------------------------------

        Stream _ReadAndPreprocessFile(string filename) // 读取并预处理文件
        {
            if (!File.Exists(filename)) // 检查文件是否存在
            {
                Messages.Error($"Script imported file {filename} not found. Scriptfile: {_ScriptInstance.Path}"); // 报告错误
                return null; // 返回空流
            }

            if (!_FileList.Contains(filename)) // 检查文件列表中是否包含该文件
            {
                _FileList.Add(filename); // 添加文件到文件列表
            }

            var ms = new MemoryStream(); // 创建内存流
            using StreamWriter sw = new StreamWriter(ms, Encoding.Unicode, 1024, true); // 创建写入器

            sw.WriteLine($"#line 1 \"{Path.GetFileName(filename)}\""); // 写入 #line 指令

            bool inComment = false; // 是否在注释中
            int lineNumber = 0; // 行号
            using (var reader = File.OpenText(filename)) // 使用文件读取器读取文件
            {
                while (!reader.EndOfStream) // 循环读取每行
                {
                    var rawLine = reader.ReadLine(); // 读取原始行
                    if (rawLine == null)
                        break;
                    var line = rawLine.Trim().ToLower(); // 去除首尾空白并转换为小写

                    lineNumber++; // 行号递增

                    if (line.IsNullOrWhiteSpace()) // 检查是否为空行
                    {
                        sw.WriteLine(rawLine); // 写入空行
                        continue;
                    }

                    // We are in a comment block
                    if (inComment) // 如果在注释块中
                    {
                        var posStart = line.LastIndexOf("/*", StringComparison.Ordinal); // 获取注释块起始位置
                        var posEnd = line.LastIndexOf("*/", StringComparison.Ordinal); // 获取注释块结束位置
                        if (posEnd > posStart) // 如果结束位置大于起始位置
                        {
                            // Comment closed
                            inComment = false; // 标记注释块关闭

                            // only continue if no code follows
                            if (!line.EndsWith("*/")) // 如果注释块后还有代码
                                continue;
                        }

                        // Comment continues
                        sw.WriteLine(rawLine); // 写入注释行
                        continue;
                    }

                    // Pragma
                    if (line.StartsWith("#")) // 如果是 #pragma 指令
                    {
                        if (line.StartsWith("#if ") || line.StartsWith("#elif ")) // 如果是条件编译指令
                        {
                            var second = line.Substring(line.IndexOf(' ') + 1).Trim(); // 获取条件表达式
                            if (second.StartsWith("version ")) // 如果是版本判断
                            {
                                var value = second.Substring(8); // 获取版本号值
                                var substitute = _ProcessVersion(value); // 处理版本号
                                if (substitute == null) // 如果处理失败
                                {
                                    Messages.Error($"Script error in line {lineNumber}: The version expression is invalid. Scriptfile: {_ScriptInstance.Path}."); // 报告错误
                                    ms.Close(); // 关闭流
                                    return null; // 返回空流
                                }

                                sw.Write(rawLine.Substring(0, rawLine.IndexOf('#'))); // 写入 #pragma 指令之前的部分
                                sw.Write(line.Substring(0, line.IndexOf(' ') + 1)); // 写入 #pragma 指令
                                sw.WriteLine(substitute + " // version " + value); // 写入替换后的版本号表达式
                                continue;
                            }
                        }

                        // non-directive for us, just skip
                        sw.WriteLine(rawLine); // 写入 #pragma 指令
                        continue;
                    }

                    // Single comment line
                    if (line.StartsWith("//")) // 如果是单行注释
                    {
                        // non-directive comment line, just skip
                        sw.WriteLine(rawLine); // 写入单行注释
                        continue;
                    }

                    // Comment starting
                    if (line.StartsWith("/*")) // 如果是注释块起始
                    {
                        var posStart = line.LastIndexOf("/*", StringComparison.Ordinal); // 获取注释块起始位置
                        var posEnd = line.LastIndexOf("*/", StringComparison.Ordinal); // 获取注释块结束位置
                        if (posEnd < posStart) // 如果结束位置小于起始位置
                        {
                            // Comment opened, but not closed
                            inComment = true; // 标记注释块开启
                        }
                        else
                        {
                            // Comment opened and closed
                            // only continue if no code follows
                            if (!line.EndsWith("*/")) // 如果注释块后还有代码
                                continue;
                        }

                        sw.WriteLine(rawLine); // 写入注释行
                        continue;
                    }

                    sw.WriteLine(rawLine); // 写入原始行
                }
            }

            return ms; // 返回内存流
        }

        //--------------------------------------------------------------------------------------------------

        static readonly char[] _VersionOperands = {'<', '>', '=', '!', ' '}; // 版本号操作符

        string _ProcessVersion(string line) // 处理版本号
        {
            var splitIndex = line.LastIndexOfAny(_VersionOperands); // 查找操作符位置
            if (splitIndex < 0) // 如果操作符位置小于 0
                return null;

            var value = line.Substring(splitIndex + 1); // 获取版本号值
            if (!float.TryParse(value, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var version)) // 解析版本号
                return null;

            bool result;
            var operand = line.Substring(0, splitIndex + 1).Trim(); // 获取操作符
            switch (operand) // 判断操作符
            {
                case "==":
                    result = version.IsEqual(_Version, 0.001f); // 判断是否相等
                    break;
                case "!=":
                    result = !version.IsEqual(_Version, 0.001f); // 判断是否不相等
                    break;
                case "<":
                    result = _Version < version; // 判断是否小于
                    break;
                case "<=":
                    result = _Version < version || version.IsEqual(_Version, 0.001f); // 判断是否小于等于
                    break;
                case ">":
                    result = _Version > version; // 判断是否大于
                    break;
                case ">=":
                    result = _Version > version || version.IsEqual(_Version, 0.001f); // 判断是否大于等于
                    break;
                default:
                    return null;
            }

            return result ? "true" : "false"; // 返回处理结果
        }

        //--------------------------------------------------------------------------------------------------

        bool _ReportResults(ImmutableArray<Diagnostic> errors) // 报告编译结果
        {
            DiagnosticSeverity severity = DiagnosticSeverity.Info; // 默认的诊断级别为 Info
            var sb = new StringBuilder(); // 创建字符串构建器
            foreach (var diag in errors) // 遍历诊断信息
            {
                if (diag.Severity < DiagnosticSeverity.Warning) // 如果诊断级别低于警告
                    continue;
                if (diag.Severity > severity) // 如果诊断级别高于当前级别
                    severity = diag.Severity; // 更新诊断级别

                if (diag.Location != null) // 如果有位置信息
                {
                    var location = diag.Location.GetMappedLineSpan(); // 获取映射的行范围
                    if (location.IsValid && !location.Path.IsNullOrEmpty()) // 如果有效且路径不为空
                    {
                        sb.Append(Path.GetFileName(location.Path)); // 添加文件名
                        sb.Append($" ({location.Span.Start.Line},{location.Span.Start.Character}): "); // 添加行号和字符位置
                    }
                }

                sb.Append(severity == DiagnosticSeverity.Error ? "Error " : "Warning "); // 添加错误或警告信息
                sb.Append(diag.Id); // 添加诊断标识
                sb.Append(": ");
                sb.AppendLine(diag.GetMessage(CultureInfo.InvariantCulture)); // 添加诊断消息
            }

            if (severity == DiagnosticSeverity.Error) // 如果诊断级别为错误
            {
                Messages.Error("Script compilation failed with errors.", sb.ToString()); // 报告编译错误
            }
            else if(severity == DiagnosticSeverity.Warning) // 如果诊断级别为警告
            {
                Messages.Warning("Script compiled with warnings.", sb.ToString()); // 报告编译警告
            }

            return severity == DiagnosticSeverity.Error; // 返回是否有错误
        }

    }
}

以上代码是一个用于编译脚本的 C# 类 ScriptCompiler,它负责读取、预处理、编译脚本文件,并提供一些额外的功能,如版本控制和诊断报告。下面是代码的注释说明:

  • ScriptCompiler 类包含了用于编译脚本的各种方法和功能。
  • Compile 方法用于编译脚本实例。
  • _Compile 方法实现了脚本的实际编译过程,包括读取、预处理文件,设置编译选项,编译脚本,并处理编译结果。
  • _ReadAndPreprocessFile 方法用于读取和预处理文件,包括处理注释、#pragma 指令和版本号条件等。
  • _ProcessVersion 方法用于处理版本号条件表达式。
  • _ReportResults 方法用于报告编译结果,包括错误和警告信息。
  • 其他部分包括一些字段和常量的定义,以及静态构造函数和一些辅助方法的实现。

这个类的主要功能是将脚本文件编译成可执行的代码,并提供错误和警告信息以供调试和修复。

cs 复制代码
using Macad.Core.Topology;

namespace Macad.Core
{
    /// <summary>
    /// 脚本上下文类,作为所有脚本的基类,在应用程序中提供对各种组件的访问。
    /// </summary>
    public class ScriptContext
    {
        /// <summary>
        /// 默认的脚本上下文实例,允许无需实例化新的对象即可访问其成员。
        /// </summary>
        internal static ScriptContext Default { get; } = new ScriptContext();

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 模型对象,包含应用程序中的所有实体和对每个实体的引用。
        /// </summary>
        public Model Document => CoreContext.Current.Document;
        
        /// <summary>
        /// 图层集合,定义了与实体的可见性、交互性和可视化相关的共享属性。
        /// </summary>
        public LayerCollection Layers => CoreContext.Current.Layers;

        /// <summary>
        /// 工作空间,包含应用程序内部用于可视化和交互的所有功能。
        /// </summary>
        public Workspace Workspace => CoreContext.Current.Workspace;
        
        /// <summary>
        /// 视口,是工作空间的窗口,用于用户在图形环境中查看和操作对象。
        /// </summary>
        public Viewport Viewport => CoreContext.Current.Viewport;
    }
}

这段代码定义了名为 ScriptContext 的类,它作为脚本的基类,在应用程序中提供对各种组件的访问。下面是对其成员的解释:

  • Default 属性

    • 提供了 ScriptContext 的静态实例,允许在不需要实例化新的 ScriptContext 对象的情况下轻松访问其成员。
  • Document 属性

    • 表示模型,其中包含应用程序中的所有实体和对每个实体的引用。通常,CAD 模型的几何结构和结构都驻留在这里。
  • Layers 属性

    • 表示图层集合,定义了与应用程序中实体的可见性、交互性和可视化相关的共享属性。图层通常用于组织和管理在视口中显示对象的方式。
  • Workspace 属性

    • 表示工作空间,其中包含应用程序内部用于可视化和交互的所有功能。这包括与 CAD 模型相关的工具、命令和其他功能。
  • Viewport 属性

    • 表示视口,它是对工作空间的窗口。视口是用户与 CAD 模型交互的地方,在图形环境中查看和操作对象。

这些属性为脚本提供了对应用程序环境中关键组件的访问,使其能够有效地执行操作并在 CAD 模型中操作数据。

cs 复制代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.Scripting;

namespace Macad.Core
{
    /// <summary>
    /// 表示一个脚本实例,可以加载、编译和运行脚本文件。
    /// </summary>
    public sealed class ScriptInstance
    {
        /// <summary>
        /// 从文件加载脚本实例。
        /// </summary>
        /// <param name="filename">脚本文件路径。</param>
        /// <param name="forceReload">是否强制重新加载。</param>
        /// <returns>加载的脚本实例。</returns>
        public static ScriptInstance LoadScriptFromFile(string filename, bool forceReload = false)
        {
            return LoadScriptFromFile(filename, ScriptContext.Default, forceReload);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 从文件加载脚本实例。
        /// </summary>
        /// <param name="filename">脚本文件路径。</param>
        /// <param name="contextInstance">脚本上下文实例。</param>
        /// <param name="forceReload">是否强制重新加载。</param>
        /// <returns>加载的脚本实例。</returns>
        public static ScriptInstance LoadScriptFromFile(string filename, ScriptContext contextInstance, bool forceReload=false)
        {
            if (!File.Exists(filename))
            {
                Messages.Error( "Script file does not exist: " + filename);
            }

            ScriptInstance scriptInstance;
            if (!forceReload)
            {
                scriptInstance = ScriptCache.Find(filename);
                if (scriptInstance != null)
                    return scriptInstance;
            }

            scriptInstance = new ScriptInstance(filename, contextInstance);
            if(ScriptCompiler.Compile(scriptInstance))
            {
                return scriptInstance;
            }
            return null;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 获取当前正在运行的脚本实例。
        /// </summary>
        public static ScriptInstance Current
        {
            get { return _RunningScripts.Any() ? _RunningScripts.Peek() : null; }
        }

        static readonly Stack<ScriptInstance> _RunningScripts = new Stack<ScriptInstance>();

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 脚本名称。
        /// </summary>
        public string Name { get; }
        
        /// <summary>
        /// 脚本文件路径。
        /// </summary>
        public string Path { get; }
        
        /// <summary>
        /// 脚本上下文实例。
        /// </summary>
        public ScriptContext ContextInstance { get; }

        //--------------------------------------------------------------------------------------------------

        ScriptRunner<object> _RunnerDelegate;

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 初始化脚本实例。
        /// </summary>
        internal ScriptInstance(string filename, ScriptContext contextInstance)
        {
            Name = System.IO.Path.GetFileName(filename);
            Path = System.IO.Path.GetFullPath(filename);
            ContextInstance = contextInstance;
            _Reset();
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 重置脚本实例。
        /// </summary>
        void _Reset()
        {
            _RunnerDelegate = null;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 设置脚本运行器委托。
        /// </summary>
        internal bool Init(ScriptRunner<object> runnerDelegate)
        {
            _Reset();

            _RunnerDelegate = runnerDelegate;

            return true;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 运行脚本。
        /// </summary>
        /// <returns>指示脚本是否成功运行。</returns>
        public bool Run()
        {
            bool result = false;
            if (_RunnerDelegate == null)
                return false;

            _RunningScripts.Push(this);
            try
            {
                _RunnerDelegate(ContextInstance).Wait();
                result = true;
            }
            catch (Exception e)
            {
                Messages.Exception($"The executed script '{Name}' throwed an exception.", e);
            }
            finally
            {
            }
            _RunningScripts.Pop();

            // Only commit after the last script has ended.
            if(_RunningScripts.Count == 0)
                CoreContext.Current?.UndoHandler?.Commit();

            return result;
        }
    }
}

以上代码是用于处理脚本的加载、编译和运行的类 ScriptInstance。下面是逐行注释和说明:

  1. using 指令:引入了一系列命名空间,包括必要的系统命名空间和 Microsoft.CodeAnalysis.Scripting 用于动态编译和运行脚本的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个脚本实例类。

  3. public sealed class ScriptInstance:定义了 ScriptInstance 类,用于表示一个脚本实例。

  4. public static ScriptInstance LoadScriptFromFile(string filename, bool forceReload = false) 方法:从文件加载脚本实例的静态方法。根据提供的文件名加载脚本,并可以选择是否强制重新加载。返回加载的脚本实例。

  5. public static ScriptInstance LoadScriptFromFile(string filename, ScriptContext contextInstance, bool forceReload=false) 方法:从文件加载脚本实例的静态方法,可以指定脚本上下文实例。返回加载的脚本实例。

  6. ScriptInstance(string filename, ScriptContext contextInstance) 构造函数:初始化脚本实例的私有构造函数。用于创建脚本实例对象。

  7. public static ScriptInstance Current 属性:获取当前正在运行的脚本实例。使用栈来存储运行的脚本实例,从而支持嵌套调用。

  8. _RunningScripts 字段:用于存储当前正在运行的脚本实例的栈。

  9. public string Name { get; } 属性:脚本名称。

  10. public string Path { get; } 属性:脚本文件路径。

  11. public ScriptContext ContextInstance { get; } 属性:脚本上下文实例。

  12. _RunnerDelegate 字段:脚本运行器委托,用于执行脚本的方法。

  13. _Reset() 方法:重置脚本实例,清空运行器委托。

  14. internal bool Init(ScriptRunner<object> runnerDelegate) 方法:初始化脚本实例,设置运行器委托。

  15. public bool Run() 方法:运行脚本的方法。使用运行器委托来执行脚本的逻辑,并处理可能发生的异常。最终返回指示脚本是否成功运行的布尔值。

以上就是 ScriptInstance 类的代码及其注释说明。

cs 复制代码
using System;
using System.Collections.Immutable;
using System.IO;
using Microsoft.CodeAnalysis;

namespace Macad.Core
{
    /// <summary>
    /// 表示用于解析脚本源文件的解析器。
    /// </summary>
    public sealed class ScriptSourceResolver : SourceFileResolver
    {
        Func<string, Stream> _ReadFunc;

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 构造函数,初始化脚本源文件解析器。
        /// </summary>
        /// <param name="baseDirectory">基本目录。</param>
        /// <param name="readFunc">读取文件的方法。</param>
        public ScriptSourceResolver(string baseDirectory, Func<string, Stream> readFunc)
            : base(ImmutableArray<string>.Empty, baseDirectory)
        {
            _ReadFunc = readFunc;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 打开指定路径的文件流。
        /// </summary>
        /// <param name="resolvedPath">已解析的文件路径。</param>
        /// <returns>文件流。</returns>
        public override Stream OpenRead(string resolvedPath)
        {
            return _ReadFunc(Path.GetFullPath(resolvedPath));
        }
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个脚本源文件解析器类。

  3. public sealed class ScriptSourceResolver : SourceFileResolver:定义了 ScriptSourceResolver 类,用于解析脚本的源文件。

  4. Func<string, Stream> _ReadFunc;:私有字段,用于存储读取文件的方法。

  5. 构造函数 ScriptSourceResolver(string baseDirectory, Func<string, Stream> readFunc):初始化脚本源文件解析器的构造函数,设置基本目录和读取文件的方法。

  6. public override Stream OpenRead(string resolvedPath) 方法:重写了基类的打开读取方法,根据解析后的文件路径调用存储的读取文件方法获取文件流。

以上是 ScriptSourceResolver 类的代码及其注释说明。

cs 复制代码
using System;
using System.Diagnostics;
using Macad.Core.Topology;

namespace Macad.Core
{
    public class DataBlobUndoAction : UndoAction
    {
        byte[] _StoredData;

        //--------------------------------------------------------------------------------------------------

        public DataBlobUndoAction(Guid instanceGuid)
        {
            InstanceGuid = instanceGuid;
        }

        //--------------------------------------------------------------------------------------------------

        public bool Set(IUndoableDataBlob dataBlobOwner)
        {
            InstanceGuid = dataBlobOwner.Guid;

            var data = dataBlobOwner.GetUndoDataBlob();
            if (data != null)
            {
                _StoredData = data;
                //Debug.WriteLine(_StoredData);
                return true;
            }
            return false;
        }

        //--------------------------------------------------------------------------------------------------

        public override void Restore(Entity instance, UndoHandler undoHandler)
        {
            var dataBlobOwner = instance as IUndoableDataBlob;
            Debug.Assert(dataBlobOwner != null);

            dataBlobOwner.SetUndoDataBlob(_StoredData);
        }
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个类的定义。

  3. public class DataBlobUndoAction : UndoAction:定义了 DataBlobUndoAction 类,表示数据块撤销操作。

  4. _StoredData:私有字段,存储撤销操作的数据块。

  5. 构造函数 DataBlobUndoAction(Guid instanceGuid):初始化具有指定实例 GUID 的 DataBlobUndoAction 类的新实例。

  6. public bool Set(IUndoableDataBlob dataBlobOwner) 方法:设置数据块的撤销操作,参数为拥有数据块的对象。

  7. public override void Restore(Entity instance, UndoHandler undoHandler) 方法:重写了基类的恢复方法,用于恢复撤销的操作。

以上是 DataBlobUndoAction 类的代码及其注释说明。

cs 复制代码
using System;

namespace Macad.Core
{
    /// <summary>
    /// 定义了可撤销数据块的接口。
    /// </summary>
    public interface IUndoableDataBlob
    {
        /// <summary>
        /// 获取数据块的 GUID。
        /// </summary>
        Guid Guid { get; }

        /// <summary>
        /// 获取数据块的撤销数据。
        /// </summary>
        /// <returns>表示数据块的字节数组。</returns>
        byte[] GetUndoDataBlob();

        /// <summary>
        /// 设置数据块的撤销数据。
        /// </summary>
        /// <param name="dataBlob">要设置的数据块的字节数组。</param>
        void SetUndoDataBlob(byte[] dataBlob);
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个接口的定义。

  3. public interface IUndoableDataBlob:定义了 IUndoableDataBlob 接口,表示可撤销数据块的接口。

  4. Guid Guid { get; } 属性:获取数据块的唯一标识符(GUID)。

  5. byte[] GetUndoDataBlob() 方法:获取数据块的撤销数据,返回表示数据块的字节数组。

  6. void SetUndoDataBlob(byte[] dataBlob) 方法:设置数据块的撤销数据,参数为要设置的数据块的字节数组。

以上是 IUndoableDataBlob 接口的代码及其注释说明。

cs 复制代码
using System;
using Macad.Core.Topology;

namespace Macad.Core
{
    /// <summary>
    /// 定义了可撤销拓扑操作的接口。
    /// </summary>
    public interface IUndoableTopology
    {
        /// <summary>
        /// 获取拓扑操作的唯一标识符(GUID)。
        /// </summary>
        Guid Guid { get; }

        /// <summary>
        /// 根据实例的 GUID 查找实体。
        /// </summary>
        /// <param name="instanceGuid">要查找的实例的 GUID。</param>
        /// <returns>找到的实体,如果未找到则为 null。</returns>
        Entity FindInstance(Guid instanceGuid);

        /// <summary>
        /// 获取实例的父实体。
        /// </summary>
        /// <param name="instance">要获取父实体的实例。</param>
        /// <returns>实例的父实体。</returns>
        Entity GetParent(Entity instance);

        /// <summary>
        /// 从撤销操作中添加子实体。
        /// </summary>
        /// <param name="instance">要添加的子实体。</param>
        /// <param name="parent">子实体的父实体。</param>
        void AddChildFromUndo(Entity instance, Entity parent);

        /// <summary>
        /// 从撤销操作中移除子实体。
        /// </summary>
        /// <param name="instance">要移除的子实体。</param>
        void RemoveChildFromUndo(Entity instance);

        /// <summary>
        /// 从撤销操作中移动子实体到新的父实体。
        /// </summary>
        /// <param name="instance">要移动的子实体。</param>
        /// <param name="newParent">子实体的新父实体。</param>
        void MoveChildFromUndo(Entity instance, Entity newParent);
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个接口的定义。

  3. public interface IUndoableTopology:定义了 IUndoableTopology 接口,表示可撤销拓扑操作的接口。

  4. Guid Guid { get; } 属性:获取拓扑操作的唯一标识符(GUID)。

  5. Entity FindInstance(Guid instanceGuid) 方法:根据实例的 GUID 查找实体。

  6. Entity GetParent(Entity instance) 方法:获取实例的父实体。

  7. void AddChildFromUndo(Entity instance, Entity parent) 方法:从撤销操作中添加子实体。

  8. void RemoveChildFromUndo(Entity instance) 方法:从撤销操作中移除子实体。

  9. void MoveChildFromUndo(Entity instance, Entity newParent) 方法:从撤销操作中移动子实体到新的父实体。

以上是 IUndoableTopology 接口的代码及其注释说明。

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Macad.Core.Topology;

namespace Macad.Core
{
    /// <summary>
    /// 表示属性的撤销操作。
    /// </summary>
    public class PropertyUndoAction : UndoAction
    {
        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 表示属性值的更改。
        /// </summary>
        class PropertyValueChange
        {
            readonly PropertyInfo _PropertyInfo; // 属性信息
            object _Value; // 属性值
            bool _IsEntityReference; // 属性是否引用了实体

            //--------------------------------------------------------------------------------------------------

            /// <summary>
            /// 构造函数,初始化 PropertyValueChange 的新实例。
            /// </summary>
            /// <param name="propertyInfo">要更改的属性信息。</param>
            public PropertyValueChange(PropertyInfo propertyInfo)
            {
                _PropertyInfo = propertyInfo;
                _IsEntityReference = typeof(Entity).IsAssignableFrom(_PropertyInfo.DeclaringType)
                                     && typeof(Entity).IsAssignableFrom(_PropertyInfo.PropertyType);
            }

            //--------------------------------------------------------------------------------------------------

            /// <summary>
            /// 判断属性是否相等。
            /// </summary>
            /// <param name="propInfo">要比较的属性信息。</param>
            /// <returns>如果属性相等,则为 true,否则为 false。</returns>
            public bool IsEqual(PropertyInfo propInfo)
            {
                return _PropertyInfo.Equals(propInfo);
            }

            //--------------------------------------------------------------------------------------------------

            /// <summary>
            /// 更新属性的值。
            /// </summary>
            /// <param name="instance">属性所在的实例。</param>
            /// <param name="value">要设置的属性值。</param>
            public void Update(object instance, object value = null)
            {
                if (value != null)
                {
                    _Value = value;
                }
                else
                {
                    if (_IsEntityReference)
                    {
                        // 仅保存引用
                        var entity = _PropertyInfo.GetValue(instance) as Entity;
                        _Value = entity?.Guid;
                    }
                    else
                    {
                        _Value = _PropertyInfo.GetValue(instance);
                    }
                }
            }

            //--------------------------------------------------------------------------------------------------

            /// <summary>
            /// 还原属性的值。
            /// </summary>
            /// <param name="instance">属性所在的实例。</param>
            public void RestoreValue(object instance)
            {
                if (_IsEntityReference)
                {
                    // 通过引用还原
                    var document = (instance as Entity)?.Document;
                    if (document != null && _Value != null && _Value is Guid guid)
                    {
                        _PropertyInfo.SetValue(instance, document.FindInstance(guid));
                    }
                    else
                    {
                        _PropertyInfo.SetValue(instance, null);
                    }
                }
                else
                {
                    _PropertyInfo.SetValue(instance, _Value);
                }
            }

            //--------------------------------------------------------------------------------------------------
        }

        //--------------------------------------------------------------------------------------------------
        //--------------------------------------------------------------------------------------------------

        readonly List<PropertyValueChange> _ChangeList = new List<PropertyValueChange>(); // 属性值更改列表

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 初始化 PropertyUndoAction 类的新实例。
        /// </summary>
        /// <param name="instanceGuid">操作实例的 GUID。</param>
        public PropertyUndoAction(Guid instanceGuid)
        {
            InstanceGuid = instanceGuid;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 设置属性的撤销操作。
        /// </summary>
        /// <param name="propertyInfo">要设置的属性信息。</param>
        /// <param name="instance">属性所在的实例。</param>
        /// <param name="value">要设置的属性值。</param>
        public void Set(PropertyInfo propertyInfo, Entity instance, object value = null)
        {
            // 注意:只保存第一个值更改,否则原始状态将会丢失。
            var change = _ChangeList.FirstOrDefault(pvc => pvc.IsEqual(propertyInfo));
                
            if(change != null)
                return;

            change = new PropertyValueChange(propertyInfo);
            change.Update(instance, value);
            _ChangeList.Add(change);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 还原属性的撤销操作。
        /// </summary>
        /// <param name="instance">操作实例。</param>
        /// <param name="undoHandler">撤销处理程序。</param>
        public override void Restore(Entity instance, UndoHandler undoHandler)
        {
            _ChangeList.ForEach( change => change.RestoreValue(instance));

            var shape = instance as Shapes.Shape;
            shape?.Invalidate();
        }

        //--------------------------------------------------------------------------------------------------

    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个类的定义。

  3. public class PropertyUndoAction : UndoAction:定义了表示属性撤销操作的 PropertyUndoAction 类。

  4. class PropertyValueChange:表示属性值的更改,是 PropertyUndoAction 类的嵌套类。

  5. readonly List<PropertyValueChange> _ChangeList = new List<PropertyValueChange>();:属性值更改列表,用于保存属性值的更改。

  6. public PropertyUndoAction(Guid instanceGuid)PropertyUndoAction 类的构造函数,初始化实例的 GUID。

  7. public void Set(PropertyInfo propertyInfo, Entity instance, object value = null):设置属性的撤销操作,记录属性值的更改。

  8. public override void Restore(Entity instance, UndoHandler undoHandler):还原属性的撤销操作,将属性值还原到之前的状态,并在需要时更新形状的无效状态。

以上是 PropertyUndoAction 类的代码及其注释说明。

cs 复制代码
using System;
using System.Diagnostics;
using Macad.Core.Topology;
using Macad.Common.Serialization;
using Macad.Core.Shapes;
using Macad.Common;

namespace Macad.Core
{
    /// <summary>
    /// 表示拓扑操作的撤销动作。
    /// </summary>
    public class TopologyUndoAction : UndoAction
    {
        string _StoredData; // 存储的数据
        UndoHandler.TopologyAction _TopoAction; // 拓扑操作类型
        Guid _ChildGuid; // 子实体的 GUID
        Guid? _ParentGuid; // 父实体的 GUID

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 初始化 TopologyUndoAction 类的新实例。
        /// </summary>
        /// <param name="instanceGuid">实例的 GUID。</param>
        public TopologyUndoAction(Guid instanceGuid)
        {
            InstanceGuid = instanceGuid;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 设置拓扑操作的撤销动作。
        /// </summary>
        /// <param name="child">子实体。</param>
        /// <param name="container">包含子实体的容器。</param>
        /// <param name="action">拓扑操作类型。</param>
        /// <returns>如果设置成功,则为 true,否则为 false。</returns>
        public bool Set(Entity child, IUndoableTopology container, UndoHandler.TopologyAction action)
        {
            _ChildGuid = child.Guid;
            InstanceGuid = container.Guid;

            _ParentGuid = null;
            var parent = container.GetParent(child);
            if (parent != null)
                _ParentGuid = parent.Guid;

            _TopoAction = action;
            _StoredData = null;

            switch (action)
            {
                case UndoHandler.TopologyAction.Added:
                    // 逆操作:移除。我们只需要实例以便稍后移除。
                    return true;

                case UndoHandler.TopologyAction.Removed:
                    // 逆操作:添加。我们需要实例、父实体和其数据以便稍后添加。
                    var data = Serializer.Serialize(child, new SerializationContext(SerializationScope.UndoRedo));
                    if (!data.IsNullOrEmpty())
                    {
                        _StoredData = data;
                        return true;
                    }
                    break;

                case UndoHandler.TopologyAction.Moved:
                    // 逆操作:移动回。我们需要实例和父实体。
                    return true;
            }

            return false;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 还原拓扑操作的撤销动作。
        /// </summary>
        /// <param name="instance">操作实例。</param>
        /// <param name="undoHandler">撤销处理程序。</param>
        public override void Restore(Entity instance, UndoHandler undoHandler)
        {
            var container = undoHandler.FindEntityInstance(InstanceGuid) as IUndoableTopology;
            Debug.Assert(container != null, "container != null");

            var child = undoHandler.FindEntityInstance(_ChildGuid);
            var parent = _ParentGuid.HasValue ? undoHandler.FindEntityInstance(_ParentGuid.Value) : null;

            switch (_TopoAction)
            {
                case UndoHandler.TopologyAction.Added:
                    Debug.Assert(child != null);

                    // 逆操作:移除。
                    container.RemoveChildFromUndo(child);
                    break;

                case UndoHandler.TopologyAction.Removed:
                    Debug.Assert(child == null);

                    // 逆操作:添加。
                    var context = new SerializationContext(SerializationScope.UndoRedo);
                    context.SetInstance(CoreContext.Current.Document);
                    context.SetInstance<IDocument>(CoreContext.Current.Document);
                    context.SetInstance(CoreContext.Current.Workspace);

                    child = Serializer.Deserialize<Entity>(_StoredData, context);
                    if (child != null)
                    {
                        container.AddChildFromUndo(child, parent);
                        
                        // 更新可视化状态
                        foreach(var entity in context.GetInstanceList<InteractiveEntity>())
                            entity.RaiseVisualChanged();
                    }
                    break;

                case UndoHandler.TopologyAction.Moved:
                    Debug.Assert(child != null);

                    // 逆操作:移动回。
                    undoHandler.AddTopologyChange(UndoHandler.TopologyAction.Moved, container, child);

                    container.MoveChildFromUndo(child, parent);
                    break;
            }
        }
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个类的定义。

  3. public class TopologyUndoAction : UndoAction:定义了表示拓扑操作的撤销动作的 TopologyUndoAction 类。

  4. string _StoredData;:存储数据,用于保存被移除的实体的数据。

  5. UndoHandler.TopologyAction _TopoAction;:拓扑操作类型,表示进行的拓扑操作。

  6. Guid _ChildGuid;:子实体的 GUID。

  7. Guid? _ParentGuid;:父实体的 GUID。

  8. public TopologyUndoAction(Guid instanceGuid)TopologyUndoAction 类的构造函数,初始化实例的 GUID。

  9. public bool Set(Entity child, IUndoableTopology container, UndoHandler.TopologyAction action):设置拓扑操作的撤销动作,记录进行的拓扑操作及相关信息。

  10. public override void Restore(Entity instance, UndoHandler undoHandler):还原拓扑操作的撤销动作,执行相应的逆操作,并在需要时更新可视化状态。

cs 复制代码
using System;
using Macad.Core.Topology;

namespace Macad.Core
{
    /// <summary>
    /// 表示撤销动作的抽象基类。
    /// </summary>
    public abstract class UndoAction
    {
        /// <summary>
        /// 获取或设置实例的 GUID。
        /// </summary>
        public Guid InstanceGuid { get; protected set; }

        /// <summary>
        /// 还原撤销动作。
        /// </summary>
        /// <param name="instance">应用撤销动作的实例。</param>
        /// <param name="undoHandler">撤销处理程序。</param>
        public abstract void Restore(Entity instance, UndoHandler undoHandler);
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个类的定义。

  3. public abstract class UndoAction:定义了表示撤销动作的抽象基类 UndoAction

  4. public Guid InstanceGuid { get; protected set; }:表示实例的 GUID 属性,用于标识要执行撤销动作的实例。

  5. public abstract void Restore(Entity instance, UndoHandler undoHandler);:抽象方法,表示还原撤销动作的操作,需要由派生类实现具体的还原逻辑。

cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Macad.Core.Topology;
using Macad.Common;
using Macad.Common.Serialization;

namespace Macad.Core
{
    /// <summary>
    /// 处理撤销和重做操作的管理器。
    /// </summary>
    public class UndoHandler : BaseObject
    {
        #region Enums

        /// <summary>
        /// 表示拓扑操作的枚举。
        /// </summary>
        public enum TopologyAction
        {
            Added,
            Removed,
            Moved
        }

        #endregion

        #region Properties

        /// <summary>
        /// 获取一个值,该值指示是否可以执行撤销操作。
        /// </summary>
        public bool CanUndo
        {
            get { return _UndoStack.Count > 0; }
        }

        /// <summary>
        /// 获取一个值,该值指示是否可以执行重做操作。
        /// </summary>
        public bool CanRedo
        {
            get { return _RedoStack.Count > 0; }
        }

        /// <summary>
        /// 获取用于存储撤销操作的栈。
        /// </summary>
        public LimitedStack<UndoAction[]> UndoStack
        {
            get { return _UndoStack; }
        }

        /// <summary>
        /// 获取用于存储重做操作的栈。
        /// </summary>
        public LimitedStack<UndoAction[]> RedoStack
        {
            get { return _RedoStack; }
        }

        #endregion

        readonly IUndoableTopology _Document;
        readonly LimitedStack<UndoAction[]> _RedoStack;
        readonly LimitedStack<UndoAction[]> _UndoStack;
        readonly List<UndoAction> _PendingActions = new List<UndoAction>();
        bool _IsRestoring;

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 初始化 <see cref="UndoHandler"/> 类的新实例。
        /// </summary>
        /// <param name="document">撤销处理器所属的文档。</param>
        public UndoHandler(IUndoableTopology document)
        {
            _Document = document;
            _UndoStack = new LimitedStack<UndoAction[]>(500);
            _RedoStack = new LimitedStack<UndoAction[]>(500);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 引发属性更改事件。
        /// </summary>
        void RaisePropertyChanged()
        {
            RaisePropertyChanged("CanUndo");
            RaisePropertyChanged("CanRedo");
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 提交挂起的撤销操作。
        /// </summary>
        public void Commit()
        {
            if (!_IsRestoring)
            {
                Commit(false);
                _RedoStack.Clear();
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 提交挂起的撤销操作。
        /// </summary>
        /// <param name="toRedoStack">指示是否提交到重做操作的栈。</param>
        void Commit(bool toRedoStack)
        {
            var stack = toRedoStack ? _RedoStack : _UndoStack;
            if (_PendingActions.Count > 0)
            {
                // 反转动作列表以便首先撤销最后一个动作
                _PendingActions.Reverse();
                stack.Push(_PendingActions.ToArray());
                _PendingActions.Clear();
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 添加属性更改的撤销操作。
        /// </summary>
        /// <param name="instance">发生属性更改的实例。</param>
        /// <param name="propertyName">更改的属性名称。</param>
        /// <param name="clone">属性的克隆值。</param>
        public void AddPropertyChange(Entity instance, [CallerMemberName] string propertyName = "", object clone = null)
        {
            var propInfo = instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            if (propInfo == null) 
                return;

            if (!propInfo.GetCustomAttributes(typeof(SerializeMemberAttribute), true).Any())
                return;

            var action = (PropertyUndoAction)_PendingActions.FirstOrDefault(a => a.InstanceGuid.Equals(instance.Guid) && a is PropertyUndoAction);
            if (action == null)
            {
                action = new PropertyUndoAction(instance.Guid);
                _PendingActions.Add(action);
            }
            action.Set(propInfo, instance, clone);
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 添加数据块更改的撤销操作。
        /// </summary>
        /// <param name="instance">数据块实例。</param>
        public void AddDataBlockChange(IUndoableDataBlob instance)
        {
            var action = (DataBlobUndoAction)_PendingActions.FirstOrDefault(a => a.InstanceGuid.Equals(instance.Guid) && a is DataBlobUndoAction);
            if (action != null)
                return;

            action = new DataBlobUndoAction(instance.Guid);
            if (action.Set(instance))
            {
                _PendingActions.Add(action);
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 添加拓扑更改的撤销操作。
        /// </summary>
        /// <param name="topologyAction">拓扑操作类型。</param>
        /// <param name="container">容器实例。</param>
        /// <param name="instance">拓扑实例。</param>
        public void AddTopologyChange(TopologyAction topologyAction, IUndoableTopology container, Entity instance)
        {
            var action = new TopologyUndoAction(container.Guid);
            if (action.Set(instance, container, topologyAction))
            {
                _PendingActions.Add(action);
            }
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 执行指定步数的撤销操作。
        /// </summary>
        /// <param name="steps">撤销步数。</param>
        public void DoUndo(int steps)
        {
            while ((steps > 0) && (_UndoStack.Count > 0))
            {
                Restore(false);
                steps--;
            }

            Commit(true);
            RaisePropertyChanged();
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 执行指定步数的重做操作。
        /// </summary>
        /// <param name="steps">重做步数。</param>
        public void DoRedo(int steps)
        {
            while ((steps > 0) && (_RedoStack.Count > 0))
            {
                Restore(true);
                steps--;
            }

            Commit(false);
            RaisePropertyChanged();
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 还原撤销操作。
        /// </summary>
        /// <param name="fromRedoStack">指示是否从重做操作的栈中还原。</param>
        void Restore(bool fromRedoStack)
        {
            var stack = fromRedoStack ? _RedoStack : _UndoStack;

            _IsRestoring = true;
            var actions = stack.Pop();
            foreach (var action in actions)
            {
                var instance = FindEntityInstance(action.InstanceGuid);
                
                // 如果未找到实例,则跳过,因为它必须是拓扑操作的一部分
                if(instance == null)
                    continue;

                instance.OnBeforeUndo();
                action.Restore(instance, this);
                instance.OnAfterUndo();
            }
            _IsRestoring = false;
        }

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 根据实例的 GUID 查找实例。
        /// </summary>
        /// <param name="instanceGuid">实例的 GUID。</param>
        /// <returns>找到的实例,如果未找到则为 null。</returns>
        public Entity FindEntityInstance(Guid instanceGuid)
        {
            return _Document.FindInstance(instanceGuid);
        }
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个类的定义。

  3. public class UndoHandler : BaseObject:定义了处理撤销和重做操作的管理器 UndoHandler,它继承自 BaseObject 类。

  4. public enum TopologyAction:定义了表示拓扑操作的枚举类型 TopologyAction,包括 Added、Removed 和 Moved。

  5. IUndoableTopology _Document:存储撤销处理器所属的文档。

  6. LimitedStack<UndoAction[]> _RedoStackLimitedStack<UndoAction[]> _UndoStack:分别用于存储重做操作和撤销操作的栈。

  7. List<UndoAction> _PendingActions:存储待提交的撤销动作列表。

  8. public UndoHandler(IUndoableTopology document):构造函数,初始化撤销处理器。

  9. void RaisePropertyChanged():引发属性更改事件。

  10. public void Commit()void Commit(bool toRedoStack):提交挂起的撤销操作。

  11. public void AddPropertyChange(Entity instance, [CallerMemberName] string propertyName = "", object clone = null):添加属性更改的撤销操作。

  12. public void AddDataBlockChange(IUndoableDataBlob instance):添加数据块更改的撤销操作。

  13. public void AddTopologyChange(TopologyAction topologyAction, IUndoableTopology container, Entity instance):添加拓扑更改的撤销操作。

  14. public void DoUndo(int steps)public void DoRedo(int steps):执行指定步数的撤销和重做操作。

  15. void Restore(bool fromRedoStack):还原撤销操作。

  16. public Entity FindEntityInstance(Guid instanceGuid):根据实例的 GUID 查找实例。

cs 复制代码
using System.IO;
using System.Text;
using Macad.Common;

namespace Macad.Core
{
    /// <summary>
    /// 抽象类,表示剪贴板操作。
    /// </summary>
    public abstract class Clipboard
    {
        /// <summary>
        /// 获取或设置当前剪贴板实例。
        /// </summary>
        public static Clipboard Current { get; protected set; }

        /// <summary>
        /// 检查剪贴板中是否包含指定格式的数据。
        /// </summary>
        /// <param name="format">数据格式。</param>
        /// <returns>如果剪贴板中包含指定格式的数据,则为 true;否则为 false。</returns>
        public abstract bool ContainsData(in string format);

        /// <summary>
        /// 从剪贴板中获取指定格式的数据流。
        /// </summary>
        /// <param name="format">数据格式。</param>
        /// <returns>数据流。</returns>
        public abstract MemoryStream GetDataStream(in string format);

        /// <summary>
        /// 将数据写入剪贴板。
        /// </summary>
        /// <param name="format">数据格式。</param>
        /// <param name="data">数据流。</param>
        public abstract void SetData(in string format, in MemoryStream data);

        //--------------------------------------------------------------------------------------------------

        /// <summary>
        /// 将字符串数据写入剪贴板。
        /// </summary>
        /// <param name="format">数据格式。</param>
        /// <param name="data">字符串数据。</param>
        public void SetData(in string format, in string data)
        {
            SetData(format, new MemoryStream(Encoding.Unicode.GetBytes(data)));
        }

        /// <summary>
        /// 从剪贴板中获取指定格式的数据并转换为字符串。
        /// </summary>
        /// <param name="format">数据格式。</param>
        /// <returns>数据字符串。</returns>
        public string GetDataAsString(in string format)
        {
            var memstream = GetDataStream(format);

            return Encoding.Unicode.GetString(memstream.ToArray());
        }
    }
}

逐行注释和说明如下:

  1. using 指令:引入了必要的命名空间。

  2. namespace Macad.Core:定义了 Macad.Core 命名空间,包含了整个类的定义。

  3. public abstract class Clipboard:定义了抽象类 Clipboard,表示剪贴板操作。

  4. public static Clipboard Current { get; protected set; }:获取或设置当前剪贴板实例。

  5. public abstract bool ContainsData(in string format):抽象方法,检查剪贴板中是否包含指定格式的数据。

  6. public abstract MemoryStream GetDataStream(in string format):抽象方法,从剪贴板中获取指定格式的数据流。

  7. public abstract void SetData(in string format, in MemoryStream data):抽象方法,将数据写入剪贴板。

  8. public void SetData(in string format, in string data):将字符串数据写入剪贴板。

  9. public string GetDataAsString(in string format):从剪贴板中获取指定格式的数据并转换为字符串。

相关推荐
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
亽仒凣凣1 小时前
Windows安装Redis图文教程
数据库·windows·redis
炫彩@之星1 小时前
Windows和Linux安全配置和加固
linux·windows·安全·系统安全配置和加固
QQ同步助手2 小时前
如何正确使用人工智能:开启智慧学习与创新之旅
人工智能·学习·百度
小奥超人2 小时前
RAR压缩算法的文件修复功能详解
windows·经验分享·winrar·办公技巧
流浪的小新2 小时前
【AI】人工智能、LLM学习资源汇总
人工智能·学习
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
南宫生11 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__11 小时前
Web APIs学习 (操作DOM BOM)
学习
Clockwiseee12 小时前
php伪协议
windows·安全·web安全·网络安全