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
}
}
这段代码定义了针对 Pnt
、Pnt2d
、Vec2d
、Dir
、Pln
、Quaternion
和 Ax22d
类型的扩展方法,提供了各种便捷的数学计算和操作功能。
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是一种用于描述实体几何形状的方法,它通过表示实体的边界以及边界之间的关系来描述几何形状。
该类的主要作用包括:
- 表示B-Rep拓扑树中的节点,每个节点代表一个几何形状元素(如面、边、点等)或由多个子节点组成的组合形状。
- 可以获取节点的名称、子节点、B-Rep形状、属性等信息。
- 提供了初始化子节点和属性列表的方法,以便在需要时进行延迟加载。
- 可以添加索引前缀以标识节点在其父节点中的位置。
- 可以根据B-Rep形状的类型添加相应类型的子节点集合,并可选择对子节点进行分组。
- 提供了获取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);
}
//--------------------------------------------------------------------------------------------------
}
}
整段代码的作用是提供了一些用于颜色和材质转换的工具方法。这些方法包括:
-
将
Quantity_Color
类型的颜色转换为Color
类型的颜色。 -
将
Quantity_NameOfColor
类型的颜色名称转换为Color
类型的颜色。 -
将
Quantity_NameOfColor
类型的颜色名称转换为Quantity_Color
类型的颜色。 -
将
Graphic3d_NameOfMaterial
类型的材质名称转换为Graphic3d_MaterialAspect
类型的材质属性。 -
将
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类型的序列化器,包括Pnt
、Pnt2d
、Vec
、Dir
、Quaternion
、Ax1
、Ax2
和Pln
。每个序列化器都实现了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>
接口,其中T
是TopoDS_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
。下面是逐行注释和说明:
-
using
指令:引入了一系列命名空间,包括必要的系统命名空间和Microsoft.CodeAnalysis.Scripting
用于动态编译和运行脚本的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个脚本实例类。 -
public sealed class ScriptInstance
:定义了ScriptInstance
类,用于表示一个脚本实例。 -
public static ScriptInstance LoadScriptFromFile(string filename, bool forceReload = false)
方法:从文件加载脚本实例的静态方法。根据提供的文件名加载脚本,并可以选择是否强制重新加载。返回加载的脚本实例。 -
public static ScriptInstance LoadScriptFromFile(string filename, ScriptContext contextInstance, bool forceReload=false)
方法:从文件加载脚本实例的静态方法,可以指定脚本上下文实例。返回加载的脚本实例。 -
ScriptInstance(string filename, ScriptContext contextInstance)
构造函数:初始化脚本实例的私有构造函数。用于创建脚本实例对象。 -
public static ScriptInstance Current
属性:获取当前正在运行的脚本实例。使用栈来存储运行的脚本实例,从而支持嵌套调用。 -
_RunningScripts
字段:用于存储当前正在运行的脚本实例的栈。 -
public string Name { get; }
属性:脚本名称。 -
public string Path { get; }
属性:脚本文件路径。 -
public ScriptContext ContextInstance { get; }
属性:脚本上下文实例。 -
_RunnerDelegate
字段:脚本运行器委托,用于执行脚本的方法。 -
_Reset()
方法:重置脚本实例,清空运行器委托。 -
internal bool Init(ScriptRunner<object> runnerDelegate)
方法:初始化脚本实例,设置运行器委托。 -
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));
}
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个脚本源文件解析器类。 -
public sealed class ScriptSourceResolver : SourceFileResolver
:定义了ScriptSourceResolver
类,用于解析脚本的源文件。 -
Func<string, Stream> _ReadFunc;
:私有字段,用于存储读取文件的方法。 -
构造函数
ScriptSourceResolver(string baseDirectory, Func<string, Stream> readFunc)
:初始化脚本源文件解析器的构造函数,设置基本目录和读取文件的方法。 -
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);
}
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个类的定义。 -
public class DataBlobUndoAction : UndoAction
:定义了DataBlobUndoAction
类,表示数据块撤销操作。 -
_StoredData
:私有字段,存储撤销操作的数据块。 -
构造函数
DataBlobUndoAction(Guid instanceGuid)
:初始化具有指定实例 GUID 的DataBlobUndoAction
类的新实例。 -
public bool Set(IUndoableDataBlob dataBlobOwner)
方法:设置数据块的撤销操作,参数为拥有数据块的对象。 -
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);
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个接口的定义。 -
public interface IUndoableDataBlob
:定义了IUndoableDataBlob
接口,表示可撤销数据块的接口。 -
Guid Guid { get; }
属性:获取数据块的唯一标识符(GUID)。 -
byte[] GetUndoDataBlob()
方法:获取数据块的撤销数据,返回表示数据块的字节数组。 -
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);
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个接口的定义。 -
public interface IUndoableTopology
:定义了IUndoableTopology
接口,表示可撤销拓扑操作的接口。 -
Guid Guid { get; }
属性:获取拓扑操作的唯一标识符(GUID)。 -
Entity FindInstance(Guid instanceGuid)
方法:根据实例的 GUID 查找实体。 -
Entity GetParent(Entity instance)
方法:获取实例的父实体。 -
void AddChildFromUndo(Entity instance, Entity parent)
方法:从撤销操作中添加子实体。 -
void RemoveChildFromUndo(Entity instance)
方法:从撤销操作中移除子实体。 -
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();
}
//--------------------------------------------------------------------------------------------------
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个类的定义。 -
public class PropertyUndoAction : UndoAction
:定义了表示属性撤销操作的PropertyUndoAction
类。 -
class PropertyValueChange
:表示属性值的更改,是PropertyUndoAction
类的嵌套类。 -
readonly List<PropertyValueChange> _ChangeList = new List<PropertyValueChange>();
:属性值更改列表,用于保存属性值的更改。 -
public PropertyUndoAction(Guid instanceGuid)
:PropertyUndoAction
类的构造函数,初始化实例的 GUID。 -
public void Set(PropertyInfo propertyInfo, Entity instance, object value = null)
:设置属性的撤销操作,记录属性值的更改。 -
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;
}
}
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个类的定义。 -
public class TopologyUndoAction : UndoAction
:定义了表示拓扑操作的撤销动作的TopologyUndoAction
类。 -
string _StoredData;
:存储数据,用于保存被移除的实体的数据。 -
UndoHandler.TopologyAction _TopoAction;
:拓扑操作类型,表示进行的拓扑操作。 -
Guid _ChildGuid;
:子实体的 GUID。 -
Guid? _ParentGuid;
:父实体的 GUID。 -
public TopologyUndoAction(Guid instanceGuid)
:TopologyUndoAction
类的构造函数,初始化实例的 GUID。 -
public bool Set(Entity child, IUndoableTopology container, UndoHandler.TopologyAction action)
:设置拓扑操作的撤销动作,记录进行的拓扑操作及相关信息。 -
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);
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个类的定义。 -
public abstract class UndoAction
:定义了表示撤销动作的抽象基类UndoAction
。 -
public Guid InstanceGuid { get; protected set; }
:表示实例的 GUID 属性,用于标识要执行撤销动作的实例。 -
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);
}
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个类的定义。 -
public class UndoHandler : BaseObject
:定义了处理撤销和重做操作的管理器UndoHandler
,它继承自BaseObject
类。 -
public enum TopologyAction
:定义了表示拓扑操作的枚举类型TopologyAction
,包括 Added、Removed 和 Moved。 -
IUndoableTopology _Document
:存储撤销处理器所属的文档。 -
LimitedStack<UndoAction[]> _RedoStack
和LimitedStack<UndoAction[]> _UndoStack
:分别用于存储重做操作和撤销操作的栈。 -
List<UndoAction> _PendingActions
:存储待提交的撤销动作列表。 -
public UndoHandler(IUndoableTopology document)
:构造函数,初始化撤销处理器。 -
void RaisePropertyChanged()
:引发属性更改事件。 -
public void Commit()
和void Commit(bool toRedoStack)
:提交挂起的撤销操作。 -
public void AddPropertyChange(Entity instance, [CallerMemberName] string propertyName = "", object clone = null)
:添加属性更改的撤销操作。 -
public void AddDataBlockChange(IUndoableDataBlob instance)
:添加数据块更改的撤销操作。 -
public void AddTopologyChange(TopologyAction topologyAction, IUndoableTopology container, Entity instance)
:添加拓扑更改的撤销操作。 -
public void DoUndo(int steps)
和public void DoRedo(int steps)
:执行指定步数的撤销和重做操作。 -
void Restore(bool fromRedoStack)
:还原撤销操作。 -
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());
}
}
}
逐行注释和说明如下:
-
using
指令:引入了必要的命名空间。 -
namespace Macad.Core
:定义了Macad.Core
命名空间,包含了整个类的定义。 -
public abstract class Clipboard
:定义了抽象类Clipboard
,表示剪贴板操作。 -
public static Clipboard Current { get; protected set; }
:获取或设置当前剪贴板实例。 -
public abstract bool ContainsData(in string format)
:抽象方法,检查剪贴板中是否包含指定格式的数据。 -
public abstract MemoryStream GetDataStream(in string format)
:抽象方法,从剪贴板中获取指定格式的数据流。 -
public abstract void SetData(in string format, in MemoryStream data)
:抽象方法,将数据写入剪贴板。 -
public void SetData(in string format, in string data)
:将字符串数据写入剪贴板。 -
public string GetDataAsString(in string format)
:从剪贴板中获取指定格式的数据并转换为字符串。