macad.interaction解析workspace

admin2024-04-03  0

1.

using Macad.Occt; // 引入Macad.Occt命名空间,提供对OpenCascade CAD内核的访问

namespace Macad.Interaction
{
    public sealed class AndSelectionFilter : ISelectionFilter // 定义名为AndSelectionFilter的类,实现了ISelectionFilter接口
    {
        readonly ISelectionFilter[] _Filters; // 声明一个只读字段_Filters,类型为ISelectionFilter数组

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

        public AndSelectionFilter(params ISelectionFilter[] filters) // 构造函数,接受可变数量的ISelectionFilter参数
        {
            _Filters = filters; // 将传入的参数赋值给_Filters字段
        }

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

        SelectMgr_Filter ISelectionFilter.GetNativeFilter() // 实现接口方法GetNativeFilter,返回SelectMgr_Filter对象
        {
            var andFilter = new SelectMgr_AndFilter(); // 创建SelectMgr_AndFilter对象
            foreach (var filter in _Filters) // 遍历_Filters数组中的每个过滤器
            {
                andFilter.Add(filter.GetNativeFilter()); // 将每个过滤器的原生过滤器添加到andFilter中
            }

            return andFilter; // 返回andFilter对象
        }

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

    }
}

总结:这段代码定义了一个名为 AndSelectionFilter 的类,用于创建多个选择过滤器的逻辑与(AND)组合。通过传入不同的选择过滤器,可以根据多个条件进行对象的选择。

2.

using System; // 引入System命名空间,提供基本的系统功能
using Macad.Occt; // 引入Macad.Occt命名空间,提供对OpenCascade CAD内核的访问

namespace Macad.Interaction
{
    public sealed class EdgeSelectionFilter : ISelectionFilter // 定义名为EdgeSelectionFilter的类,实现了ISelectionFilter接口
    {
        public enum EdgeType // 定义名为EdgeType的枚举,表示边的类型
        {
            Any,    // 任意类型的边
            Line,   // 直线
            Circle, // 圆
        }

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

        readonly EdgeType _EdgeType = EdgeType.Any; // 声明只读字段_EdgeType,类型为EdgeType,默认值为Any

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

        public EdgeSelectionFilter(EdgeType edgeType) // 构造函数,接受EdgeType参数
        {
            _EdgeType = edgeType; // 将传入的参数赋值给_EdgeType字段
        }

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

        SelectMgr_Filter ISelectionFilter.GetNativeFilter() // 实现接口方法GetNativeFilter,返回SelectMgr_Filter对象
        {
            return new StdSelect_EdgeFilter(_GetNativeTypeOfEdge(_EdgeType)); // 返回一个基于_EdgeType的原生边过滤器
        }
        
        //--------------------------------------------------------------------------------------------------

        StdSelect_TypeOfEdge _GetNativeTypeOfEdge(EdgeSelectionFilter.EdgeType type) // 定义一个私有方法,将EdgeType转换为StdSelect_TypeOfEdge
        {
            switch (type) // 根据传入的EdgeType类型进行判断
            {
                case EdgeType.Any: // 如果是Any类型的边
                    return StdSelect_TypeOfEdge.AnyEdge; // 返回AnyEdge类型
                case EdgeType.Line: // 如果是Line类型的边
                    return StdSelect_TypeOfEdge.Line; // 返回Line类型
                case EdgeType.Circle: // 如果是Circle类型的边
                    return StdSelect_TypeOfEdge.Circle; // 返回Circle类型
                default: // 如果传入的类型不是上述三种之一
                    throw new ArgumentOutOfRangeException(nameof(type), type, null); // 抛出参数越界异常
            }
        }
    }
}

总结:这段代码定义了一个名为 EdgeSelectionFilter 的类,用于创建用于选择边的选择过滤器。该类包含一个枚举 EdgeType,用于表示边的不同类型,包括任意类型、直线和圆。通过传入不同的 EdgeType,可以创建对应类型的边选择过滤器。

3.

using System; // 引入System命名空间,提供基本的系统功能
using Macad.Occt; // 引入Macad.Occt命名空间,提供对OpenCascade CAD内核的访问

namespace Macad.Interaction
{
    public sealed class FaceSelectionFilter : ISelectionFilter // 定义名为FaceSelectionFilter的类,实现了ISelectionFilter接口
    {
        public enum FaceType // 定义名为FaceType的枚举,表示面的类型
        {
            Any,        // 任意类型的面
            Plane,      // 平面
            Cylinder,   // 圆柱面
            Sphere,     // 球面
            Torus,      // 圆环面
            Revolution, // 旋转面
            Cone        // 圆锥面
        }

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

        readonly FaceType _FaceType = FaceType.Any; // 声明只读字段_FaceType,类型为FaceType,默认值为Any

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

        public FaceSelectionFilter(FaceType faceType) // 构造函数,接受FaceType参数
        {
            _FaceType = faceType; // 将传入的参数赋值给_FaceType字段
        }

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

        SelectMgr_Filter ISelectionFilter.GetNativeFilter() // 实现接口方法GetNativeFilter,返回SelectMgr_Filter对象
        {
            return new StdSelect_FaceFilter(_GetNativeTypeOfFace(_FaceType)); // 返回一个基于_FaceType的原生面过滤器
        }
        
        //--------------------------------------------------------------------------------------------------

        StdSelect_TypeOfFace _GetNativeTypeOfFace(FaceType type) // 定义一个私有方法,将FaceType转换为StdSelect_TypeOfFace
        {
            switch (type) // 根据传入的FaceType类型进行判断
            {
                case FaceType.Any:        // 如果是Any类型的面
                    return StdSelect_TypeOfFace.AnyFace;        // 返回AnyFace类型
                case FaceType.Plane:      // 如果是Plane类型的面
                    return StdSelect_TypeOfFace.Plane;          // 返回Plane类型
                case FaceType.Cylinder:   // 如果是Cylinder类型的面
                    return StdSelect_TypeOfFace.Cylinder;       // 返回Cylinder类型
                case FaceType.Sphere:     // 如果是Sphere类型的面
                    return StdSelect_TypeOfFace.Sphere;         // 返回Sphere类型
                case FaceType.Torus:      // 如果是Torus类型的面
                    return StdSelect_TypeOfFace.Torus;          // 返回Torus类型
                case FaceType.Revolution: // 如果是Revolution类型的面
                    return StdSelect_TypeOfFace.Revol;          // 返回Revol类型
                case FaceType.Cone:       // 如果是Cone类型的面
                    return StdSelect_TypeOfFace.Cone;           // 返回Cone类型
                default: // 如果传入的类型不是上述七种之一
                    throw new ArgumentOutOfRangeException(nameof(type), type, null); // 抛出参数越界异常
            }
        }
    }
}

总结:这段代码定义了一个名为 FaceSelectionFilter 的类,用于创建用于选择面的选择过滤器。该类包含一个枚举 FaceType,用于表示面的不同类型,包括任意类型、平面、圆柱面、球面、圆环面、旋转面和圆锥面。通过传入不同的 FaceType,可以创建对应类型的面选择过滤器。

4.

using System.Collections.Generic; // 引入System.Collections.Generic命名空间,提供泛型集合类的支持
using System.Linq; // 引入System.Linq命名空间,提供LINQ查询功能
using Macad.Common; // 引入Macad.Common命名空间
using Macad.Interaction.Visual; // 引入Macad.Interaction.Visual命名空间
using Macad.Occt; // 引入Macad.Occt命名空间
using Macad.Occt.Extensions; // 引入Macad.Occt.Extensions命名空间

namespace Macad.Interaction
{
    public class InstanceSelectionFilter : ISelectionFilter // 定义名为InstanceSelectionFilter的类,实现了ISelectionFilter接口
    {
        readonly AIS_InteractiveObject[] _Instances; // 声明只读字段_Instances,类型为AIS_InteractiveObject数组,用于存储实例对象

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

        public InstanceSelectionFilter(IEnumerable<AIS_InteractiveObject> instances) // 构造函数,接受AIS_InteractiveObject类型的集合作为参数
        {
            _Instances = instances.ToArray(); // 将传入的实例对象集合转换为数组并赋值给_Instances字段
        }
        
        //--------------------------------------------------------------------------------------------------

        public InstanceSelectionFilter(IEnumerable<VisualObject> instances) // 构造函数,接受VisualObject类型的集合作为参数
        {
            _Instances = instances.Select(obj => obj.AisObject).ToArray(); // 将传入的VisualObject集合的AisObject属性提取出来转换为数组并赋值给_Instances字段
        }

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

        SelectMgr_Filter ISelectionFilter.GetNativeFilter() // 实现接口方法GetNativeFilter,返回SelectMgr_Filter对象
        {
            AISX_InstanceFilter filter = new(); // 创建AISX_InstanceFilter对象
            _Instances.ForEach(filter.Add); // 将_Instances数组中的每个元素添加到过滤器中
            return filter; // 返回过滤器
        }
    }
}

总结:这段代码定义了一个名为 InstanceSelectionFilter 的类,用于创建用于选择实例的选择过滤器。该类实现了 ISelectionFilter 接口。它包含两个构造函数,一个接受 IEnumerable<AIS_InteractiveObject> 类型的实例集合,另一个接受 IEnumerable<VisualObject> 类型的实例集合。通过这两个构造函数,可以传入不同类型的实例对象集合来创建对应的选择过滤器。然后,通过实现 GetNativeFilter 方法,返回一个 SelectMgr_Filter 对象,其中包含了要选择的实例对象。

5.

using Macad.Occt; // 引入Macad.Occt命名空间

namespace Macad.Interaction
{
    public interface ISelectionFilter // 定义名为ISelectionFilter的接口
    {
        SelectMgr_Filter GetNativeFilter(); // 声明一个返回SelectMgr_Filter类型的GetNativeFilter方法
    }
}

总结:这段代码定义了一个名为 ISelectionFilter 的接口,其中包含一个方法 GetNativeFilter,用于获取原生的选择过滤器。该接口用于定义选择过滤器的标准,以便在交互操作中使用不同的过滤器实现。

6.

using Macad.Occt; // 引入Macad.Occt命名空间

namespace Macad.Interaction
{
    public sealed class OrSelectionFilter : ISelectionFilter // 定义名为OrSelectionFilter的类,实现ISelectionFilter接口
    {
        readonly ISelectionFilter[] _Filters; // 声明一个名为_Filters的只读ISelectionFilter数组字段

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

        public OrSelectionFilter(params ISelectionFilter[] filters) // 构造函数,接受一个可变长度的ISelectionFilter数组作为参数
        {
            _Filters = filters; // 将参数赋值给_Filters字段
        }

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

        SelectMgr_Filter ISelectionFilter.GetNativeFilter() // 实现接口中的GetNativeFilter方法
        {
            var andFilter = new SelectMgr_OrFilter(); // 创建SelectMgr_OrFilter实例
            foreach (var filter in _Filters) // 遍历_Filters数组中的每个选择过滤器
            {
                andFilter.Add(filter.GetNativeFilter()); // 将每个选择过滤器的原生过滤器添加到orFilter中
            }

            return andFilter; // 返回组合后的orFilter
        }
    }
}

总结:这段代码定义了一个名为 OrSelectionFilter 的类,实现了 ISelectionFilter 接口。该类用于创建一个“或”组合的选择过滤器,即如果任何一个子过滤器返回 true,则整体条件为 true。

7.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting;
using Macad.Interaction.Visual;
using Macad.Core;
using Macad.Core.Shapes;
using Macad.Core.Topology;
using Macad.Occt;

namespace Macad.Interaction
{
    public sealed class SelectionContext : IDisposable
    {
        [Flags]
        public enum Options
        {
            None = 0,
            IncludeAll = 1 << 0,
            NewSelectedList = 1 << 1,
        }

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

        public List<InteractiveEntity> SelectedEntities { get; } // 存储选中的实体的列表

        readonly WorkspaceController _WorkspaceController; // 工作空间控制器
        readonly HashSet<VisualObject> _InOrExcludedShapes = new(); // 存储要包含或排除的可视对象的哈希集合
        readonly Options _Options; // 选择上下文的选项
        bool _IsActive = false; // 选择上下文是否处于活动状态
        SubshapeTypes _SubshapeTypes; // 子形状的类型
        ISelectionFilter _SelectionFilter; // 选择过滤器

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

        // 构造函数,接受工作空间控制器和选项作为参数
        public SelectionContext(WorkspaceController workspaceController, Options options)
        {
            _WorkspaceController = workspaceController;
            _Options = options;
            
            if (_Options.HasFlag(Options.NewSelectedList)) // 如果选项中包含NewSelectedList,则创建一个新的选中实体列表
            {
                SelectedEntities = new List<InteractiveEntity>();
            }
        }

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

        public void Dispose()
        {
            if (_IsActive) // 如果选择上下文处于活动状态
            {
                VisualObject.AisObjectChanged -= _VisualObject_AisObjectChanged; // 取消注册VisualObject.AisObjectChanged事件的处理程序
                _IsActive = false; // 将活动状态标志设置为false
            }
        }

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

        // 设置子形状选择的类型
        public void SetSubshapeSelection(SubshapeTypes enabledTypes)
        {
            if (_SubshapeTypes == enabledTypes)
                return;

            _SubshapeTypes = enabledTypes; // 设置子形状的类型
            _RaiseParametersChanged(); // 触发参数已更改事件
        }

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

        // 设置选择过滤器
        public void SetSelectionFilter(ISelectionFilter filter)
        {
            _SelectionFilter = filter; // 设置选择过滤器
            _UpdateFilter(); // 更新过滤器
        }

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

        #region In-/Exclusion

        // 包含特定实体
        public void Include(InteractiveEntity entity)
        {
            var visShape = _WorkspaceController.VisualObjects.Get(entity, true); // 获取与实体关联的可视对象
            if(visShape != null)
                Include(visShape); // 调用重载的Include方法
        }

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

        // 包含特定可视对象
        public void Include(VisualObject visObject)
        {
            if (_Options.HasFlag(Options.IncludeAll)) // 如果选项中包含IncludeAll,则将要排除的可视对象从_InOrExcludedShapes集合中移除
            {
                if (_InOrExcludedShapes.Contains(visObject))
                {
                    _InOrExcludedShapes.Remove(visObject);
                    _RaiseParametersChanged(); // 触发参数已更改事件
                }
                return;
            }

            if (_InOrExcludedShapes.Contains(visObject)) // 如果_InOrExcludedShapes集合中已包含可视对象,则直接返回
                return;

            _InOrExcludedShapes.Add(visObject); // 否则将可视对象添加到_InOrExcludedShapes集合中
            _RaiseParametersChanged(); // 触发参数已更改事件
        }

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

        // 排除特定实体
        public void Exclude(InteractiveEntity entity)
        {
            var visObject = _WorkspaceController.VisualObjects.Get(entity, true); // 获取与实体关联的可视对象
            if (visObject != null)
                Exclude(visObject); // 调用重载的Exclude方法
        }

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

        // 排除特定可视对象
        public void Exclude(VisualObject visObject)
        {
            if (!_Options.HasFlag(Options.IncludeAll)) // 如果选项中不包含IncludeAll,则将要排除的可视对象添加到_InOrExcludedShapes集合中
            {
                if (_InOrExcludedShapes.Contains(visObject))
                {
                    _InOrExcludedShapes.Remove(visObject);
                    _RaiseParametersChanged(); // 触发参数已更改事件
                }
                return;
            }

            if (_InOrExcludedShapes.Contains(visObject)) // 如果_InOrExcludedShapes集合中已包含可视对象,则直接返回
                return;

            _InOrExcludedShapes.Add(visObject); // 否则将可视对象添加到_InOrExcludedShapes集合中
            _RaiseParametersChanged(); // 触发参数已更改事件
        }

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

        #endregion

        #region AIS Context Control

        // 激活选择上下文
        public void Activate()
        {
            Debug.Assert(!_IsActive); // 确保选择上下文当前处于非活动状态

            VisualObject.AisObjectChanged += _VisualObject_AisObjectChanged; // 注册VisualObject.AisObjectChanged事件的处理程序

            _UpdateFilter(); // 更新过滤器

            _IsActive = true; // 将活动状态标志设置为true
        }

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

        // 停用选择上下文
        public void DeActivate()
        {
            if (!_IsActive) // 如果选择上下文不处于活动状态,则直接返回
                return;

            VisualObject.AisObjectChanged -= _VisualObject_AisObjectChanged; // 取消注册VisualObject.AisObjectChanged事件的处理程序

            var aisContext = _WorkspaceController?.Workspace?.AisContext; // 获取AIS上下文
            aisContext?.RemoveFilters(); // 移除所有过滤器

            _IsActive = false; // 将活动状态标志设置为false
        }

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

        // 更新AIS上下文
        public void UpdateAis()
        {
            // 更新形状
            var visShapes = _WorkspaceController.VisualObjects.GetAll(); // 获取所有可视对象
            foreach (var visualShape in visShapes)
            {
                _UpdateShape(visualShape); // 更新形状
            }
        }

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

        // 更新特定可视对象的形状
        public void UpdateShape(VisualObject visualObject)
        {
            _UpdateShape(visualObject); // 更新形状
        }

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

        // 更新形状
        void _UpdateShape(VisualObject visualObject)
        {
            var aisContext = _WorkspaceController?.Workspace?.AisContext; // 获取AIS上下文
            var aisObject = visualObject?.AisObject; // 获取可视对象关联的对象
            if (aisContext == null || aisObject == null) // 如果AIS上下文或关联的对象为空,则直接返回
                return;

            bool includeByDefault = _Options.HasFlag(Options.IncludeAll); // 按默认情况是否包含
            bool isInOrExcluded = _InOrExcludedShapes.Contains(visualObject); // 是否在包含或排除列表中
            bool activate = includeByDefault ? !isInOrExcluded : isInOrExcluded; // 是否应该激活

            if (visualObject.IsSelectable) // 如果可视对象是可选择的
            {
                // 获取已激活的模式
                var colActivatedModes = new TColStd_ListOfInteger(); // 创建一个整型列表
                aisContext.ActivatedModes(aisObject, colActivatedModes); // 获取已激活的模式
                var activatedModes = colActivatedModes.ToList(); // 将列表转换为列表

                // 枚举所有请求的模式
                var modesToBeActivated = new bool[5]; // 创建一个包含5个布尔值的数组
                var snapHandler = _WorkspaceController.SnapHandler; // 获取工作空间的SnapHandler

                modesToBeActivated[0] = activate && _SubshapeTypes == SubshapeTypes.None; // 如果应激活且子形状类型为None,则激活
                modesToBeActivated[SubshapeType.Vertex.ToAisSelectionMode()] = activate && _SubshapeTypes.HasFlag(SubshapeTypes.Vertex) // 如果应激活且子形状类型为Vertex,则激活
                                                                               || snapHandler.NeedActiveSubshapes(SubshapeType.Vertex); // 或者如果SnapHandler需要激活子形状,则激活
                modesToBeActivated[SubshapeType.Edge.ToAisSelectionMode()] = activate && _SubshapeTypes.HasFlag(SubshapeTypes.Edge)  // 如果应激活且子形状类型为Edge,则激活
                                                                             || snapHandler.NeedActiveSubshapes(SubshapeType.Edge); // 或者如果SnapHandler需要激活子形状,则激活
                modesToBeActivated[SubshapeType.Wire.ToAisSelectionMode()] = activate && _SubshapeTypes.HasFlag(SubshapeTypes.Wire)  // 如果应激活且子形状类型为Wire,则激活
                                                                             || snapHandler.NeedActiveSubshapes(SubshapeType.Wire); // 或者如果SnapHandler需要激活子形状,则激活
                modesToBeActivated[SubshapeType.Face.ToAisSelectionMode()] = activate && _SubshapeTypes.HasFlag(SubshapeTypes.Face)  // 如果应激活且子形状类型为Face,则激活
                                                                             || snapHandler.NeedActiveSubshapes(SubshapeType.Face); // 或者如果SnapHandler需要激活子形状,则激活

                // 停用未请求的所有模式
                foreach (var mode in activatedModes)
                {
                    if(!modesToBeActivated[mode])
                        aisContext.SetSelectionModeActive(aisObject, mode, false, AIS_SelectionModesConcurrency.Multiple); // 设置选择模式为不活跃
                }
            
                // 激活所有请求的模式
                for (int mode = 0; mode < 5; mode++)
                {
                    if(modesToBeActivated[mode])
                        aisContext.SetSelectionModeActive(aisObject, mode, true, AIS_SelectionModesConcurrency.Multiple); // 设置选择模式为活跃
                }
            }
            else
            {
                aisContext.Deactivate(aisObject); // 如果不可选择,则停用可视对象
            }
        }

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

        // 当可视对象的AIS对象发生变化时调用
        void _VisualObject_AisObjectChanged(VisualObject visualObject)
        {
            if (visualObject.AisObject != null)
            {
                UpdateShape(visualObject); // 更新形状
            }
        }

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

        #endregion

        #region Events

        public delegate void ParametersChangedEventHandler(SelectionContext selectionContext); // 参数更改事件处理程序委托

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

        public event ParametersChangedEventHandler ParametersChanged; // 参数更改事件

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

        void _RaiseParametersChanged()
        {
            ParametersChanged?.Invoke(this); // 触发参数更改事件
        }


        #endregion

    }
}

总结:这段代码定义了一个名为 SelectionContext 的类,用于管理交互式对象的选择上下文。它包含了选择的实体列表、工作空间控制器、选项、子形状类型、选择过滤器等信息。该类提供了一系列方法用于激活、停用选择上下文,设置子形状选择类型,设置选择过滤器,包含或排除特定实体或可视对象,并更新AIS上下文中的形状。

8.

namespace Macad.Interaction
{
    public static class SelectionFilterExtensions
    {
        // 将多个选择过滤器组合成逻辑或操作的选择过滤器
        public static ISelectionFilter Or(this ISelectionFilter filter, params ISelectionFilter[] others)
        {
            ISelectionFilter[] filters = new ISelectionFilter[others.Length + 1]; // 创建一个长度为其他选择过滤器数量加1的数组
            filters[0] = filter; // 将当前选择过滤器作为数组的第一个元素
            others.CopyTo(filters, 1); // 将其他选择过滤器复制到数组中
            return new OrSelectionFilter(filters); // 返回一个逻辑或操作的选择过滤器
        }    
        
        //--------------------------------------------------------------------------------------------------
        
        // 将多个选择过滤器组合成逻辑与操作的选择过滤器
        public static ISelectionFilter And(this ISelectionFilter filter, params ISelectionFilter[] others)
        {
            ISelectionFilter[] filters = new ISelectionFilter[others.Length + 1]; // 创建一个长度为其他选择过滤器数量加1的数组
            filters[0] = filter; // 将当前选择过滤器作为数组的第一个元素
            others.CopyTo(filters, 1); // 将其他选择过滤器复制到数组中
            return new AndSelectionFilter(filters); // 返回一个逻辑与操作的选择过滤器
        }
        
        //--------------------------------------------------------------------------------------------------

    }
}

总结:SelectionFilterExtensions 类定义了两个扩展方法 OrAnd,用于将多个选择过滤器组合成逻辑或操作和逻辑与操作的选择过滤器。这些方法通过创建 OrSelectionFilterAndSelectionFilter 类的实例来实现组合操作。

9.

namespace Macad.Interaction
{
    public sealed class SelectionManager : IDisposable
    {
        // 选择模式枚举
        public enum SelectionMode
        {
            Exclusive, // 排他模式
            Add,       // 添加模式
            Toggle     // 切换模式
        }

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

        // 已选择的实体列表
        public List<InteractiveEntity> SelectedEntities
        {
            get
            {
                for (var i = _SelectionContexts.Count - 1; i >= 0; i--)
                {
                    var sel = _SelectionContexts[i].SelectedEntities;
                    if (sel != null)
                        return sel;
                }

                return _BaseContext.SelectedEntities;
            }
        }

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

        // 静态空列表
        static readonly List<InteractiveEntity> _EmptyList = new(0);
        readonly WorkspaceController _WorkspaceController;

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

        // 构造函数
        public SelectionManager(WorkspaceController workspaceController)
        {
            _WorkspaceController = workspaceController;
            _BaseContext = new SelectionContext(workspaceController, SelectionContext.Options.IncludeAll | SelectionContext.Options.NewSelectedList);
            _BaseContext.Activate();
            Entity.EntityRemoved += _Entity_EntityRemoved;
        }

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

        // 销毁方法
        public void Dispose()
        {
            Entity.EntityRemoved -= _Entity_EntityRemoved;
            _BaseContext.Dispose();
            SelectedEntities.Clear();
        }

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

        #region Selection

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

        // 清除选择
        public void ClearSelection()
        {
            ChangeEntitySelection(_EmptyList, SelectedEntities);
        }

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

        // 选择实体
        public void SelectEntity(InteractiveEntity entity, SelectionMode mode = SelectionMode.Exclusive)
        {
            SelectEntities(new []{entity}, mode);
        }

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

        // 选择多个实体
        public void SelectEntities(IEnumerable<InteractiveEntity> entities, SelectionMode mode = SelectionMode.Exclusive)
        {
            List<InteractiveEntity> toSelect = null;
            List<InteractiveEntity> toDeselect = null;
            entities ??= _EmptyList;

            switch (mode)
            {
                case SelectionMode.Exclusive:
                    toSelect = entities.Except(SelectedEntities).ToList();
                    toDeselect = SelectedEntities.Except(entities).ToList();
                    break;
                case SelectionMode.Add:
                    toSelect = entities.Except(SelectedEntities).ToList();
                    toDeselect = _EmptyList;
                    break;
                case SelectionMode.Toggle:
                    toSelect = entities.Except(SelectedEntities).ToList();
                    toDeselect = SelectedEntities.Intersect(entities).ToList();
                    break;
            }

            ChangeEntitySelection(toSelect, toDeselect);
        }
        
        //--------------------------------------------------------------------------------------------------

        // 取消选择实体
        public void DeselectEntity(InteractiveEntity entity)
        {
            DeselectEntities(new []{entity});
        }

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

        // 取消选择多个实体
        public void DeselectEntities(IEnumerable<InteractiveEntity> entities)
        {
            if (entities == null)
                return;

            List<InteractiveEntity> toDeselect = entities.Intersect(SelectedEntities).ToList();
            if (toDeselect.Count == 0)
                return;

            ChangeEntitySelection(_EmptyList, toDeselect);
        }

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

        // 更改实体选择
        [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
        public void ChangeEntitySelection(IEnumerable<InteractiveEntity> entitiesToSelect, IEnumerable<InteractiveEntity> entitiesToUnSelect)
        {
            if (_RaiseSelectionChanging(entitiesToSelect, entitiesToUnSelect))
                return;

            entitiesToUnSelect.ForEach(_RemoveEntityFromSelectionList);
            entitiesToSelect.ForEach(_AddEntityToSelectionList);

            _SyncToAisSelection();
            _RaiseSelectionChanged();
        }

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

        // 将实体添加到选择列表
        void _AddEntityToSelectionList(InteractiveEntity entity)
        {
            if (entity == null || SelectedEntities.Contains(entity))
                return;

            SelectedEntities.Add(entity);
        }

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

        // 从选择列表中移除实体
        void _RemoveEntityFromSelectionList(InteractiveEntity entity)
        {
            if (entity == null)
                return;

            SelectedEntities.Remove(entity);
        }

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

        // 从 AIS 选择中同步到选择列表
        void _SyncFromAisSelection()
        {
            var aisContext = _WorkspaceController.Workspace.AisContext;
            var aisSelected = new List<InteractiveEntity>();

            aisContext.InitSelected();
            while (aisContext.MoreSelected())
            {
                var selected = _WorkspaceController.VisualObjects.GetEntity(aisContext.SelectedInteractive());
                if (selected != null)
                    aisSelected.Add(selected);

                aisContext.NextSelected();
            }

            var toSelect = aisSelected.Except(SelectedEntities).ToArray();
            var toDeselect = SelectedEntities.Except(aisSelected).ToArray();

            if (_RaiseSelectionChanging(toSelect, toDeselect))
                return;

            foreach (var entity in toDeselect)
            {
                _RemoveEntityFromSelectionList(entity);
            }

            foreach (var entity in toSelect)
            {
                _AddEntityToSelectionList(entity);
            }

            _RaiseSelectionChanged();
        }

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

        // 从选择列表同步到 AIS 选择
        [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
        void _SyncToAisSelection()
        {
            var aisContext = _WorkspaceController.Workspace.AisContext;
            _WorkspaceController.VisualObjects.UpdateInvalidatedEntities();

            aisContext.ClearSelected(false);
            var aisObjToSelect = SelectedEntities.Select(entity => _WorkspaceController.VisualObjects.Get(entity)?.AisObject)
                                                 .Where(vo => vo != null);
            foreach (var selected in aisObjToSelect)
            {
                aisContext.AddOrRemoveSelected(selected, false);
            }
            aisContext.UpdateSelected(false);
            aisContext.ClearDetected(false);
        }

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

        // 选择变更取消事件参数类
        public class SelectionChangingCancelEventArgs : CancelEventArgs
        {
            public IEnumerable<InteractiveEntity> EntitiesToUnSelect { get; }
            public IEnumerable<InteractiveEntity> EntitiesToSelect { get; }

            public SelectionChangingCancelEventArgs(IEnumerable<InteractiveEntity> entitiesToSelect, IEnumerable<InteractiveEntity> entitiesToUnSelect)
            {
                EntitiesToSelect = entitiesToSelect ?? new List<InteractiveEntity>();
                EntitiesToUnSelect = entitiesToUnSelect ?? new List<InteractiveEntity>();
            }
        }

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

        // 选择变更事件委托
        public delegate void SelectionChangingEventHandler(SelectionManager selectionManager, SelectionChangingCancelEventArgs eventArgs);

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

        // 选择变更事件
        public event SelectionChangingEventHandler SelectionChanging;

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

        // 触发选择变更事件
        bool _RaiseSelectionChanging(IEnumerable<InteractiveEntity> entitiesToSelect, IEnumerable<InteractiveEntity> entitiesToUnSelect)
        {
            if (SelectionChanging != null)
            {
                var eventArgs = new SelectionChangingCancelEventArgs(entitiesToSelect, entitiesToUnSelect);
                SelectionChanging.Invoke(this, eventArgs);
                return eventArgs.Cancel;
            }
            return false;
        }

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

        // 选择变更完成事件委托
        public delegate void SelectionChangedEventHandler(SelectionManager selectionManager);

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

        // 选择变更完成事件
        public event SelectionChangedEventHandler SelectionChanged;

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

        // 触发选择变更完成事件
        void _RaiseSelectionChanged()
        {
            SelectionChanged?.Invoke(this);
        }

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

        // 实体被移除事件处理方法
        void _Entity_EntityRemoved(Entity entity)
        {
            if (!(entity is InteractiveEntity interactiveEntity))
                return;

            // Deselect
            _RemoveEntityFromSelectionList(interactiveEntity);
            _RaiseSelectionChanged();
        }

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

        #endregion

        #region SelectionContext

        readonly SelectionContext _BaseContext;
        readonly List<SelectionContext> _SelectionContexts = new List<SelectionContext>();

        // 当前上下文属性
        public SelectionContext CurrentContext {  get { return _SelectionContexts.LastOrDefault(); } }

        bool _ContextUpdatePending = false;

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

        // 打开新的选择上下文
        public SelectionContext OpenContext(SelectionContext.Options options = SelectionContext.Options.None)
        {
            (CurrentContext ?? _BaseContext).DeActivate();

            var context = new SelectionContext(_WorkspaceController, options);
            context.ParametersChanged += _Context_ParametersChanged;
            _SelectionContexts.Add(context);
            context.Activate();
            _SyncToAisSelection();
            Invalidate();
            return context;
        }

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

        // 关闭选择上下文
        public void CloseContext(SelectionContext context)
        {
            if (_SelectionContexts.Contains(context))
            {
                bool wasCurrent = context == CurrentContext;
                context.DeActivate();
                context.ParametersChanged -= _Context_ParametersChanged;
                _SelectionContexts.Remove(context);
                if (wasCurrent)
                {
                    (CurrentContext ?? _BaseContext).Activate();
                    _SyncToAisSelection();
                }
            }
            Invalidate();
        }

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

        // 上下文参数变更事件处理方法
        void _Context_ParametersChanged(SelectionContext selectionContext)
        {
            Invalidate();
        }

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

        // 无效化方法
        public void Invalidate()
        {
            _ContextUpdatePending = true;
        }

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

        // 更新方法
        public void Update()
        {
            if (!_ContextUpdatePending)
                return;

            var ctx = CurrentContext ?? _BaseContext;
            ctx.UpdateAis();

            _ContextUpdatePending = false;
        }

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

        #endregion
    }
}

总结:SelectionManager 类实现了选择管理器,用于管理交互式实体的选择操作。它包括选择模式、选择列表的操作、选择上下文的管理以及与AIS上下文的同步等功能。通过事件 SelectionChangingSelectionChanged 实现了选择变更前后的通知。同时,通过 SelectionContext 类管理了多个选择上下文,支持在不同的选择模式下进行选择操作。

10.

using Macad.Occt;

namespace Macad.Interaction
{
    // 选择标识结构体
    public struct SelectionSignature
    {
        // 交互式对象类型
        public AIS_KindOfInteractive Kind { get; }

        // 签名
        public int Signature { get; }

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

        // 构造函数
        public SelectionSignature(AIS_KindOfInteractive kind, int signature)
        {
            Kind = kind;
            Signature = signature;
        }
    }
}

总结:SelectionSignature 结构体用于表示选择的标识,其中包括交互式对象的类型和签名信息。

11.

using Macad.Occt;

namespace Macad.Interaction
{
    // 签名选择过滤器类
    public class SignatureSelectionFilter : ISelectionFilter
    {
        // 选择标识
        readonly SelectionSignature _Signature;

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

        // 构造函数,通过传入交互式对象类型和签名来创建过滤器
        public SignatureSelectionFilter(AIS_KindOfInteractive kind, int signature)
        {
            _Signature = new(kind, signature);
        }

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

        // 构造函数,通过传入选择标识对象来创建过滤器
        public SignatureSelectionFilter(SelectionSignature signature)
        {
            _Signature = signature;
        }

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

        // 获取本地过滤器
        SelectMgr_Filter ISelectionFilter.GetNativeFilter()
        {
            return new AIS_SignatureFilter(_Signature.Kind, _Signature.Signature);
        }
    }
}

总结:SignatureSelectionFilter 类是一个实现了 ISelectionFilter 接口的选择过滤器,用于根据选择标识来过滤选择的交互式对象。

12.

using Macad.Core;
using Macad.Occt;

namespace Macad.Interaction
{
    // 子形状类型选择过滤器类
    public sealed class SubshapeTypeSelectionFilter : ISelectionFilter
    {
        // 子形状类型
        readonly SubshapeType _SubshapeType;

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

        // 构造函数,通过传入子形状类型来创建过滤器
        public SubshapeTypeSelectionFilter(SubshapeType subshapeType)
        {
            _SubshapeType = subshapeType;
        }

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

        // 获取本地过滤器
        SelectMgr_Filter ISelectionFilter.GetNativeFilter()
        {
            return new StdSelect_ShapeTypeFilter(_SubshapeType.ToTopAbs());
        }

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

    }
}

总结:SubshapeTypeSelectionFilter 类是一个实现了 ISelectionFilter 接口的选择过滤器,用于根据子形状类型来过滤选择的形状对象。

13.

using Macad.Occt;

namespace Macad.Interaction
{
    // 顶点选择过滤器类
    public sealed class VertexSelectionFilter : ISelectionFilter
    {
        // 顶点类型枚举
        public enum VertexType
        {
            All  // 所有类型的顶点
        }

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

        // 顶点类型,默认为所有类型的顶点
        readonly VertexType _VertexType = VertexType.All;

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

        // 构造函数,通过传入顶点类型来创建过滤器
        public VertexSelectionFilter(VertexType vertexType)
        {
            _VertexType = vertexType;
        }

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

        // 获取本地过滤器
        SelectMgr_Filter ISelectionFilter.GetNativeFilter()
        {
            return new StdSelect_ShapeTypeFilter(TopAbs_ShapeEnum.VERTEX);
        }

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

    }
}

总结:VertexSelectionFilter 类是一个实现了 ISelectionFilter 接口的选择过滤器,用于过滤选择的顶点对象。目前仅支持过滤所有类型的顶点。

14.

using System.ComponentModel;
using Macad.Interaction.Editors.Shapes;
using Macad.Common;
using Macad.Common.Serialization;

namespace Macad.Interaction
{
    // 编辑器状态类,继承自 BaseObject
    [SerializeType]
    public class EditorState : BaseObject
    {
        #region Active Tools

        // 当前激活的工具名称
        public string ActiveTool
        {
            get { return _ActiveTool; }
            set
            {
                RaisePropertyChanged();
            }
        }

        string _ActiveTool;

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

        // 更新当前激活的工具
        void _UpdateActiveTool(Tool tool)
        {
            _ActiveTool = tool?.Id ?? "";
            RaisePropertyChanged(nameof(ActiveTool));

            _UpdateSketchEditTool(tool as SketchEditorTool);
        }

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

        #endregion

        #region Sketch
        
        //--------------------------------------------------------------------------------------------------

        // 是否显示草图组
        public bool SketchGroupVisible
        {
            get { return _SketchGroupVisible; }
            private set
            {
                _SketchGroupVisible = value;
                RaisePropertyChanged();
            }
        }

        bool _SketchGroupVisible;

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

        // 当前激活的草图工具名称
        public string ActiveSketchTool
        {
            get { return _ActiveSketchTool; }
            private set
            {
                _ActiveSketchTool = value;
                RaisePropertyChanged();
            }
        }

        string _ActiveSketchTool;

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

        // 是否启用草图裁剪平面
        public bool SketchClipPlaneEnabled
        {
            get { return _CurrentSketchEditorTool?.ClipPlaneEnabled ?? false; }
        }

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

        SketchEditorTool _CurrentSketchEditorTool;
        
        // 更新草图编辑工具
        void _UpdateSketchEditTool(SketchEditorTool sketchEditorTool)
        {
            if (_CurrentSketchEditorTool != null)
            {
                _CurrentSketchEditorTool.PropertyChanged -= SketchEditorToolPropertyChanged;
                _CurrentSketchEditorTool = null;
                ActiveSketchTool = "";
                SketchGroupVisible = false;
            }

            if (sketchEditorTool != null)
            {
                SketchGroupVisible = true;
                _CurrentSketchEditorTool = sketchEditorTool;
                _CurrentSketchEditorTool.PropertyChanged += SketchEditorToolPropertyChanged;
                RaisePropertyChanged(nameof(SketchClipPlaneEnabled));
            }
        }

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

        // 草图编辑工具属性变更处理程序
        void SketchEditorToolPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (_CurrentSketchEditorTool != sender) return;

            if (e.PropertyName == nameof(SketchEditorTool.CurrentTool))
            {
                if (_CurrentSketchEditorTool.CurrentTool is SketchSegmentLineCreator)
                {
                    ActiveSketchTool = _CurrentSketchEditorTool.ContinuesSegmentCreation ? "SketchSegmentPolyLineCreator" : "SketchSegmentLineCreator";
                }
                else
                {
                    ActiveSketchTool = _CurrentSketchEditorTool.CurrentTool?.GetType().Name ?? "";
                }
            }
            else if (e.PropertyName == nameof(SketchEditorTool.ClipPlaneEnabled))
            {
                RaisePropertyChanged(nameof(SketchClipPlaneEnabled));
            }
        }

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

        #endregion

        #region Transform

        // 变换的枢轴点
        [SerializeMember]
        public TransformTool.PivotPoint TransformPivot
        {
            get { return _TransformPivot; }
            set
            {
                _TransformPivot = value;
                RaisePropertyChanged();
            }
        }

        TransformTool.PivotPoint _TransformPivot;

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

        // 变换的选项
        [SerializeMember]
        public TransformTool.Options TransformOptions
        {
            get { return _TransformOptions; }
            set
            {
                _TransformOptions = value;
                RaisePropertyChanged();
            }
        }

        TransformTool.Options _TransformOptions;

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

        #endregion

        #region Selection

        // 橡皮筋选择模式
        [SerializeMember]
        public ViewportController.RubberbandSelectionMode RubberbandSelectionMode
        {
            get { return _RubberbandSelectionMode; }
            set
            {
                _RubberbandSelectionMode = value; 
                RaisePropertyChanged();
            }
        }

        ViewportController.RubberbandSelectionMode _RubberbandSelectionMode;

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

        // 是否包含接触到的对象
        public bool RubberbandIncludeTouched
        {
            get { return _RubberbandIncludeTouched; }
            set
            {
                _RubberbandIncludeTouched = value;
                RaisePropertyChanged();
            }
        }

        bool _RubberbandIncludeTouched;

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

        #endregion

        #region Workspace

        WorkspaceController _WorkspaceController;

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

        // 工作区控制器属性变更处理程序
        void _WorkspaceController_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(WorkspaceController.CurrentTool))
            {
                _UpdateActiveTool((sender as WorkspaceController)?.CurrentTool);
            }
        }

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

        #endregion

        #region Snapping

        // 是否启用网格捕捉
        [SerializeMember]
        public bool SnapToGridSelected
        {
            get { return _SnapToGridSelected; }
            set
            {
                if (_SnapToGridSelected != value)
                {
                    _SnapToGridSelected = value;
                    RaisePropertyChanged();
                }
            }
        }

        bool _SnapToGridSelected;

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

        // 是否启用顶点捕捉
        [SerializeMember]
        public bool SnapToVertexSelected
        {
            get { return _SnapToVertexSelected; }
            set
            {
                if (_SnapToVertexSelected != value)
                {
                    _SnapToVertexSelected = value;
                    RaisePropertyChanged();
                    _WorkspaceController?.Selection?.Invalidate();
                }
            }
        }

        bool _SnapToVertexSelected;

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

        // 是否启用边捕捉
        [SerializeMember]
        public bool SnapToEdgeSelected
        {
            get { return _SnapToEdgeSelected; }
            set
            {
                if (_SnapToEdgeSelected != value)
                {
                    _SnapToEdgeSelected = value;
                    RaisePropertyChanged();
                    _WorkspaceController?.Selection?.Invalidate();
                }
            }
        }

        bool _SnapToEdgeSelected;

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

        // 是否启用捕捉
        [SerializeMember]
        public bool SnappingEnabled
        {
            get { return _SnappingEnabled; }
            set
            {
                if (_SnappingEnabled != value)
                {
                    _SnappingEnabled = value;
                    RaisePropertyChanged();
                    _WorkspaceController?.Selection?.Invalidate();
                }
            }
        }

        bool _SnappingEnabled;

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

        // 捕捉像素容差
        [SerializeMember]
        public double SnappingPixelTolerance
        {
            get { return _SnappingPixelTolerance; }
            set
            {
                _WorkspaceController?.Workspace?.AisContext?.SetPixelTolerance((int)value);
                if (_SnappingPixelTolerance != value)
                {
                    _SnappingPixelTolerance = value;
                    RaisePropertyChanged();
                }
            }
        }

        double _SnappingPixelTolerance = 2.0;

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

        #endregion

        #region c'tor and property handling

        // 构造函数
        public EditorState()
        {
            InteractiveContext.Current.PropertyChanged += _InteractiveContext_PropertyChanged;

            _WorkspaceController = InteractiveContext.Current.WorkspaceController;
            if (_WorkspaceController != null)
            {
                _WorkspaceController.PropertyChanged += _WorkspaceController_PropertyChanged;
            }
        }

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

        // 交互上下文属性变更处理程序
        void _InteractiveContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "WorkspaceController")
            {
                if (_WorkspaceController != null)
                {
                    _WorkspaceController.PropertyChanged -= _WorkspaceController_PropertyChanged;
                }
                _WorkspaceController = InteractiveContext.Current.WorkspaceController;
                if (_WorkspaceController != null)
                {
                    _WorkspaceController.PropertyChanged += _WorkspaceController_PropertyChanged;
                }

                _WorkspaceController_PropertyChanged(_WorkspaceController, new PropertyChangedEventArgs(nameof(WorkspaceController.CurrentTool)));
            }
        }

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

        #endregion
    }
}

总结:EditorState 类是一个包含了编辑器状态的类,用于跟踪编辑器的各种状态,包括当前激活的工具、草图相关状态、变换选项、选择模式、捕捉设置等。

15.

using Macad.Core.Topology;

namespace Macad.Interaction
{
    // 交互克隆选项类,继承自 CloneOptions
    internal class InteractiveCloneOptions : CloneOptions
    {
        // 克隆引用体
        public override bool CloneReferencedBodies { 
            get {
                // 如果尚未询问过是否克隆引用体,则询问用户
                if(!_AskedForCloneReferencedBodies)
                    CloneReferencedBodies = AskForCloneReferencedBodies();
                return base.CloneReferencedBodies;
            }
        }

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

        // 是否被取消
        public bool IsCanceled { get; private set; }

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

        // 是否已询问过是否克隆引用体
        bool _AskedForCloneReferencedBodies;

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

        // 构造函数,初始化基类为禁止克隆引用体
        public InteractiveCloneOptions() 
            : base(false)
        {
        }

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

        // 询问是否克隆引用体
        bool AskForCloneReferencedBodies()
        {
            _AskedForCloneReferencedBodies = true;

            // 弹出询问对话框
            var dlgResult = Dialogs.Dialogs.AskBodyCloneBehaviour();
            if (!dlgResult.HasValue)
            {
                // 如果用户取消,则标记为取消
                IsCanceled = true;
                return false;
            }
            return dlgResult.Value;
        }

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

    }
}

总结:

  • InteractiveCloneOptions 类继承自 CloneOptions,用于提供交互式克隆操作的选项。
  • CloneReferencedBodies 属性重写了基类的属性,以延迟询问用户是否克隆引用体。
  • _AskedForCloneReferencedBodies 字段用于跟踪是否已经询问过用户。
  • AskForCloneReferencedBodies 方法用于实际询问用户是否克隆引用体,并根据用户的选择进行处理。

16.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using Macad.Common;
using Macad.Core;
using Macad.Interaction.Panels;

namespace Macad.Interaction
{
    // 交互式上下文类,继承自 CoreContext
    public abstract class InteractiveContext : CoreContext
    {
        // 文档控制器
        public ModelController DocumentController
        {
            get { return _DocumentController; }
            protected set
            {
                _DocumentController = value;
                RaisePropertyChanged();
            }
        }

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

        // 工作区控制器
        public override Workspace Workspace
        {
            get
            {
                return base.Workspace;
            }
            protected set
            {
                WorkspaceController = value == null ? null : new WorkspaceController(value);
                base.Workspace = value;
                RaisePropertyChanged(nameof(WorkspaceController));
            }
        }

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

        // 工作区控制器
        public WorkspaceController WorkspaceController
        {
            get { return _WorkspaceController; }
            private set
            {
                if (_WorkspaceController == value)
                    return;

                _WorkspaceController?.Dispose();
                _WorkspaceController = value;
            }
        }

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

        // 视图控制器
        public override Viewport Viewport
        {
            get
            {
                return base.Viewport;
            }
            protected set
            {
                base.Viewport = value;
                if (value == null)
                {
                    ViewportController = null;
                    WorkspaceController.ActiveViewport = null;
                }
                else
                {
                    WorkspaceController.ActiveViewport = base.Viewport;
                    ViewportController = WorkspaceController.GetViewController(base.Viewport);
                }
            }
        }

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

        // 视图控制器
        public ViewportController ViewportController
        {
            get
            {
                return _ViewportController;
            }
            private set
            {
                _ViewportController = value;
                RaisePropertyChanged();
            }
        }

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

        // 属性面板管理器
        public IPropertyPanelManager PropertyPanelManager { get; set; }

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

        // 最近使用的颜色列表
        public IList<Color> RecentUsedColors
        {
            get
            {
                if (_RecentUsedColors == null)
                    _RecentUsedColors = LoadLocalSettings<List<Color>>("RecentUsedColors") ?? new List<Color>();

                return _RecentUsedColors;
            }
        }

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

        // 编辑器状态
        public EditorState EditorState
        {
            get
            {
                if(_EditorState == null)
                    _EditorState = LoadLocalSettings<EditorState>("EditorState") ?? new EditorState();

                return _EditorState;
            }
        }
        
        //--------------------------------------------------------------------------------------------------

        // 快捷键处理器
        public ShortcutHandler ShortcutHandler { get; }

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

        ViewportController _ViewportController;
        ModelController _DocumentController;
        List<Color> _RecentUsedColors;
        EditorState _EditorState;
        WorkspaceController _WorkspaceController;

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


        // 构造函数
        protected InteractiveContext()
        {
            InteractionModule.Initialize();
            Current = this;
            DocumentController = new ModelController();
            ShortcutHandler = new ShortcutHandler();
        }

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

        // 释放资源
        protected override void Dispose(bool disposing)
        {
            WorkspaceController?.Dispose();
            WorkspaceController = null;
            ViewportController = null;
            base.Dispose(disposing);
        }

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

        #region Statics

        // 当前交互式上下文
        public new static InteractiveContext Current { get; private set; }

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

        // 保存设置
        public override void SaveSettings()
        {
            base.SaveSettings();

            if(_RecentUsedColors != null)
                SaveLocalSettings("RecentUsedColors", _RecentUsedColors);
            if(EditorState != null)
                SaveLocalSettings("EditorState", EditorState);
        }

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

        #region ScriptMru
        
        // 最近使用的脚本列表
        public ObservableCollection<string> RecentUsedScripts
        {
            get
            {
                if (_RecentUsedScripts == null)
                    _RecentUsedScripts = LoadLocalSettings<ObservableCollection<string>>("RecentUsedScripts") ?? new ObservableCollection<string>();

                return _RecentUsedScripts;
            }
        }

        const int _MaxScriptMruCount = 1;

        ObservableCollection<string> _RecentUsedScripts;

        //--------------------------------------------------------------------------------------------------
        
        // 添加到最近使用的脚本列表
        internal void AddToScriptMruList(string filePath)
        {
            var recentList = RecentUsedScripts;
            var index = recentList.IndexOfFirst(s => s.CompareIgnoreCase(filePath) == 0);
            if (index >= 0)
            {
                // 移动到列表顶部
                recentList.Move(index, 0);
                recentList[0] = filePath;
                return;
            }

            if(recentList.Count >= _MaxScriptMruCount)
                recentList.RemoveAt(recentList.Count-1);

            recentList.Insert(0, filePath);

            SaveLocalSettings("RecentUsedScripts", recentList);
        }

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

        #endregion
    }
}

总结:

  • InteractiveContext 类是交互式上下文的抽象基类,继承自 CoreContext
  • 它管理了文档控制器、工作区控制器、视图控制器、属性面板管理器等核心对象。
  • RecentUsedColors 属性存储了最近使用的颜色列表。
  • EditorState 属性存储了编辑器的状态信息。
  • RecentUsedScripts 属性存储了最近使用的脚本文件列表。
  • AddToScriptMruList 方法用于将文件路径添加到最近使用的脚本列表中。

17.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Shell;
using Macad.Interaction.Dialogs;
using Microsoft.Win32;
using Macad.Common;
using Macad.Core;
using Macad.Common.Serialization;
using Macad.Core.Topology;
using Macad.Presentation;

namespace Macad.Interaction
{
    // 模型控制器类,继承自 BaseObject
    public class ModelController : BaseObject
    {
        #region Member variables

        // 最近使用的文件列表
        public ObservableCollection<string> MruList { get; }

        #endregion

        #region Initialization

        // 构造函数
        public ModelController()
        {
            // 加载最近使用的文件列表
            MruList = InteractiveContext.Current.LoadLocalSettings<ObservableCollection<string>>("MRU")
                      ?? new ObservableCollection<string>();
            while (MruList.Count >= _MaxMruCount)
            {
                MruList.RemoveAt(MruList.Count-1);
            }

            // 订阅模型附加数据保存事件
            Model.AdditionalDataSaving += _Model_AdditionalDataSaving;
        }

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

        #endregion

        #region Model

        // 创建新模型
        public Model NewModel()
        {
            Model newModel = new();
            InteractiveContext.Current.Document = newModel;
            newModel.ResetUnsavedChanges();
            return newModel;
        }

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

        // 创建新模型并保存到指定路径
        public Model CreateModelAs(string baseDirectory = null)
        {
            var dlg = new SaveFileDialog()
            {
                Title = "Create Model...",
                CheckPathExists = true,
                Filter = "Macad3D Models|*." + Model.FileExtension,
                DefaultExt = Model.FileExtension,
            };
            if (!(baseDirectory is null))
            {
                dlg.InitialDirectory = baseDirectory;
            }

            var result = dlg.ShowDialog(Application.Current.MainWindow);
            if (!(result ?? false))
                return null;

            var relativeFilePath = dlg.FileName;
            var model = NewModel();
            if (model.SaveToFile(relativeFilePath))
            {
                return model;
            }
            else
            {
                ErrorDialogs.CannotSaveFile(dlg.FileName);
            }
            return null;
        }

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

        // 打开模型
        public bool OpenModel(string filePath)
        {
            try
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                var context = new SerializationContext(SerializationScope.Storage);
                var model = Model.CreateFromFile(filePath, context);
                if (model == null)
                {
                    switch (context.Result)
                    {
                        case SerializationResult.VersionMismatch:
                            ErrorDialogs.FileVersionIsNewer(filePath);
                            break;
                        default:
                            ErrorDialogs.CannotLoadFile(filePath);
                            break;
                    }

                    return false;
                }

                if (context.HasErrors)
                {
                    ErrorDialogs.FileLoadedWithErrors(filePath);
                }

                InteractiveContext.Current.Document = model;

                model.ResetUnsavedChanges();
                AddToMruList(filePath);

                stopwatch.Stop();
                Messages.Info(string.Format("Model " + model.Name + " loaded in {0}:{1} seconds.", stopwatch.Elapsed.Seconds, stopwatch.Elapsed.Milliseconds));
                return true;
            }
            catch (Exception e)
            {
                Messages.Exception($"Exception while loading model {filePath}.", e);
                ErrorDialogs.CannotLoadFile(filePath);
                return false;
            }
        }

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

        // 保存模型
        public bool SaveModel()
        {
            var model = InteractiveContext.Current.Document;
            if (model.FilePath.IsNullOrEmpty())
            {
                return SaveModelAs();
            }
            else
            {
                if (model.Save())
                {
                    AddToMruList(model.FilePath);
                    return true;
                }
                ErrorDialogs.CannotSaveFile(model.FilePath);
            }
            return false;
        }

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

        // 另存为模型
        public bool SaveModelAs()
        {
            var dlg = new SaveFileDialog()
            {
                Title = "Saving Model...",
                InitialDirectory = Path.GetDirectoryName(InteractiveContext.Current.Document.FilePath),
                FileName = Path.GetFileName(InteractiveContext.Current.Document.FilePath),
                CheckPathExists = true,
                Filter = "Macad3D Models|*." + Model.FileExtension,
                DefaultExt = Model.FileExtension
            };
            var result = dlg.ShowDialog(Application.Current.MainWindow);
            if (result ?? false)
            {
                var filePath = dlg.FileName;
                if (PathUtils.GetExtensionWithoutPoint(filePath).ToLower() != Model.FileExtension)
                {
                    filePath += "." + Model.FileExtension;
                }
                var model = InteractiveContext.Current.Document;
                if (model.SaveToFile(filePath))
                {
                    AddToMruList(model.FilePath);
                    return true;
                }
                else
                {
                    ErrorDialogs.CannotSaveFile(dlg.FileName);
                }
            }
            return false;
        }

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

        // 从指定目录打开模型
        public bool OpenModelFrom(string initialDirectory)
        {
            var dlg = new OpenFileDialog()
            {
                Title = "Open Model...",
                CheckFileExists = true,
                Filter = "Macad3D Models|*." + Model.FileExtension,
                DefaultExt = Model.FileExtension,
                InitialDirectory = initialDirectory ?? String.Empty
            };
            var result = dlg.ShowDialog(Application.Current.MainWindow);
            if (!((bool) result))
            {
                return false;
            }

            return OpenModel(dlg.FileName);
        }

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

        // 询问是否保存模型的更改
        public bool AskForSavingModelChanges()
        {
            if (InteractiveContext.Current.Document == null)
                return true;

            if (InteractiveContext.Current.Document.HasUnsavedChanges)
            {
                switch (Dialogs.Dialogs.AskForSavingModelChanges())
                {
                    case TaskDialogResults.Cancel:
                        return false;

                    case TaskDialogResults.Yes:
                        if (!SaveModel())
                        {
                            return false;
                        }
                        break;

                    case TaskDialogResults.No:
                        break;
                }
            }
            return true;
        }

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

        #endregion

        #region Generic File I/O

        // 打开文件
        public void OpenFile(string filePath, bool mergeToCurrent)
        {
            var extension = PathUtils.GetExtensionWithoutPoint(filePath);
            if (extension != null && extension.Equals(Model.FileExtension))
            {
                // Load model
                if (AskForSavingModelChanges())
                {
                    OpenModel(filePath);
                }
                return;
            }

            // Try importer
            var importer = ExchangeRegistry.FindExchanger<IBodyImporter>(extension);
            if (importer == null)
                return;

            if (!mergeToCurrent)
            {
                if (!AskForSavingModelChanges())
                {
                    return;
                }
            }

            // Call for Settings
            if (!ExchangerSettings.Execute<IBodyImporter>(importer))
                return;

            // Do it
            using (new ProcessingScope(null, "Importing file..."))
            {
                if (!mergeToCurrent)
                {
                    NewModel();
                }

                if (importer.DoImport(filePath, out var newBodies))
                {
                    foreach (var newBody in newBodies)
                    {
                        CoreContext.Current?.Document?.Add(newBody);
                    }

                    InteractiveContext.Current.ViewportController.ZoomFitAll();
                    AddToMruList(filePath);
                }

                CoreContext.Current.UndoHandler.Commit();
            }
        }

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

        // 添加到最近使用的文件列表
        public void AddToMruList(string filePath)
        {
            var index = MruList.IndexOfFirst(s => s.CompareIgnoreCase(filePath) == 0);
            if (index >= 0)
            {
                // Move to top of list
                MruList.Move(index, 0);
                MruList[0] = filePath;
            }
            else
            {
                if(MruList.Count >= _MaxMruCount)
                    MruList.RemoveAt(MruList.Count-1);

                MruList.Insert(0, filePath);
            }

            InteractiveContext.Current.SaveLocalSettings("MRU", MruList);

            try
            {
                JumpList.AddToRecentCategory(filePath);
            }
            catch
            {
                // ignored
            }
        }

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

        // 从最近使用的文件列表中移除
        public void RemoveFromMruList(string filePath)
        {
            var index = MruList.IndexOfFirst(s => s.CompareIgnoreCase(filePath) == 0);
            if (index >= 0)
            {
                MruList.RemoveAt(index);
            }

            InteractiveContext.Current.SaveLocalSettings("MRU", MruList);
        }
        
        //--------------------------------------------------------------------------------------------------

        // 保存模型附加数据
        void _Model_AdditionalDataSaving(Document<InteractiveEntity> sender, FileSystem fileSystem)
        {
            if (InteractiveContext.Current?.Document != sender)
                return; // This model is not active

            var bitmap = InteractiveContext.Current?.ViewportController.RenderToBitmap(500, 500);
            if (bitmap == null)
                return;

            using (var ms = new MemoryStream())
            {
                bitmap.Save(ms, ImageFormat.Png);
                fileSystem.Write("thumbnail.png", ms.GetBuffer());
            }
        }

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

        #endregion

        #region Delete, Duplicate, Clipboard
            
        const string ClipboardContentFormat = "Macad.ModelContent.1";
        
        [SerializeType]
        public class ClipboardHeader
        {
            [SerializeMember]
            public Guid ModelGuid { get; set; }
        }

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

        // 是否可以删除
        internal bool CanDelete(List<InteractiveEntity> entities)
        {
            return entities.Any() && entities.All(e => CoreContext.Current.Document.Contains(e));
        }

        //--------------------------------------------------------------------------------------------------
        
        // 删除实体
        internal void Delete(List<InteractiveEntity> entities)
        {
            if (!CanDelete(entities))
                return;

            // Delete all bodies. Use array copy, since the list will change.
            var entitiesToDelete = entities.ToArray();
            InteractiveContext.Current.WorkspaceController.Selection.ChangeEntitySelection(new InteractiveEntity[0], entitiesToDelete);
            InteractiveContext.Current.Document.SafeDelete(entitiesToDelete);
        }

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

        // 是否可以复制
        internal bool CanDuplicate(List<InteractiveEntity> entities)
        {
            return entities.Any() && entities.All(e => CoreContext.Current.Document.Contains(e));
        }

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

        // 复制实体
        internal IEnumerable<InteractiveEntity> Duplicate(List<InteractiveEntity> entities, CloneOptions options = null)
        {
            if (!CanDuplicate(entities))
                return null;

            var context = new SerializationContext(SerializationScope.CopyPaste);
            context.SetInstance(InteractiveContext.Current.Document);
            context.SetInstance<IDocument>(InteractiveContext.Current.Document);
            var serialized = Serializer.Serialize(entities, context);

            context = new SerializationContext(SerializationScope.CopyPaste);
            context.SetInstance(InteractiveContext.Current.Document);
            context.SetInstance<IDocument>(InteractiveContext.Current.Document);
            context.SetInstance(ReadOptions.RecreateGuids);
            var cloneOptions = options ?? new InteractiveCloneOptions();
            context.SetInstance<CloneOptions>(cloneOptions);
            var cloned = Serializer.Deserialize<InteractiveEntity[]>(serialized, context);

            if ((cloneOptions as InteractiveCloneOptions)?.IsCanceled ?? false)
                return null;

            foreach (var entity in context.GetInstanceList<InteractiveEntity>())
            {
                InteractiveContext.Current.Document.Add(entity);
                entity.RaiseVisualChanged();
            }

            InteractiveContext.Current.WorkspaceController.Selection.SelectEntities(cloned);
            return cloned;
        }

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

        // 是否可以复制到剪贴板
        internal bool CanCopyToClipboard(List<InteractiveEntity> entities)
        {
            return entities.Any() && entities.All(e => e is InteractiveEntity && CoreContext.Current.Document.Contains(e));
        }

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

        // 复制到剪贴板
        internal void CopyToClipboard(List<InteractiveEntity> entities)
        {
            if (!CanCopyToClipboard(entities))
                return;

            var context = new SerializationContext(SerializationScope.CopyPaste);
            context.SetInstance(InteractiveContext.Current.Document);
            context.SetInstance<IDocument>(InteractiveContext.Current.Document);
            var document = new ClipboardHeader()
            {
                ModelGuid = CoreContext.Current.Document.Guid
            };
            var writer = new Writer();
            if (!Serializer.Serialize(writer, document, context)
                || !Serializer.Serialize(writer, entities, context))
                return;

            Core.Clipboard.Current?.SetData(ClipboardContentFormat, writer.ToString());
        }

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

        // 是否可以从剪贴板粘贴
        internal bool CanPasteFromClipboard()
        {
            return Core.Clipboard.Current?.ContainsData(ClipboardContentFormat) ?? false;
        }

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

        // 从剪贴板粘贴
        internal IEnumerable<InteractiveEntity> PasteFromClipboard()
        {
            var serialized = Core.Clipboard.Current?.GetDataAsString(ClipboardContentFormat);
            if (serialized == null)
                return null;

            var context = new SerializationContext(SerializationScope.CopyPaste);
            context.SetInstance(InteractiveContext.Current.Document);
            context.SetInstance<IDocument>(InteractiveContext.Current.Document);

            var reader = new Reader(serialized, ReadOptions.RecreateGuids);
            var document = Serializer.Deserialize<ClipboardHeader>(reader, context);
            if (document == null)
                return null;

            // Same Model -> Ask for cloning
            // Foreign Model -> Clone always
            context.SetInstance<CloneOptions>(
                document.ModelGuid == CoreContext.Current.Document.Guid 
                ? new InteractiveCloneOptions() 
                : new CloneOptions(true));

            var cloned = Serializer.Deserialize<InteractiveEntity[]>(reader, context);
            foreach (var entity in context.GetInstanceList<InteractiveEntity>())
            {
                InteractiveContext.Current.Document.Add(entity);
                entity.RaiseVisualChanged();
            }

            InteractiveContext.Current.WorkspaceController?.Selection?.SelectEntities(cloned);
            return cloned;
        }

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

        #endregion
    }
}

该代码定义了一个模型控制器类,用于管理模型的创建、打开、保存、导入、复制、粘贴、删除等操作。每个方法都进行了详细的注释,说明了其功能和用法。

18.

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using Macad.Core;
using Macad.Core.Topology;
using Macad.Occt;

namespace Macad.Interaction
{
    /// <summary>
    /// 鼠标事件处理器接口。
    /// </summary>
    public interface IMouseEventHandler
    {
        /// <summary>
        /// 处理鼠标移动事件。
        /// </summary>
        /// <param name="data">鼠标事件数据。</param>
        /// <returns>如果事件已处理,则为 true;否则为 false。</returns>
        bool OnMouseMove(MouseEventData data);

        /// <summary>
        /// 处理鼠标按下事件。
        /// </summary>
        /// <param name="data">鼠标事件数据。</param>
        /// <returns>如果事件已处理,则为 true;否则为 false。</returns>
        bool OnMouseDown(MouseEventData data);

        /// <summary>
        /// 处理鼠标释放事件。
        /// </summary>
        /// <param name="data">鼠标事件数据。</param>
        /// <returns>如果事件已处理,则为 true;否则为 false。</returns>
        bool OnMouseUp(MouseEventData data);
    }

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

    /// <summary>
    /// 包含鼠标事件信息的类。
    /// </summary>
    public class MouseEventData
    {
        /// <summary>
        /// 与鼠标事件关联的视口。
        /// </summary>
        public Viewport Viewport { get; private set; }

        /// <summary>
        /// 鼠标事件发生的屏幕点。
        /// </summary>
        public Point ScreenPoint { get; private set; }

        /// <summary>
        /// 鼠标事件发生的原始三维空间点。
        /// </summary>
        public Pnt RawPoint { get; private set; }

        /// <summary>
        /// 鼠标事件发生的平面点。
        /// </summary>
        public Pnt PointOnPlane { get; private set; }

        /// <summary>
        /// 鼠标事件期间按下的修饰键。
        /// </summary>
        public ModifierKeys ModifierKeys { get; set; }

        /// <summary>
        /// 在鼠标事件期间检测到的 AIS 交互对象列表。
        /// </summary>
        public List<AIS_InteractiveObject> DetectedAisInteractives { get; } = new();

        /// <summary>
        /// 在鼠标事件期间检测到的交互实体列表。
        /// </summary>
        public List<InteractiveEntity> DetectedEntities { get; } = new();

        /// <summary>
        /// 在鼠标事件期间检测到的形状列表。
        /// </summary>
        public List<TopoDS_Shape> DetectedShapes { get; } = new();

        /// <summary>
        /// 指示是否强制重新检测鼠标事件期间的实体。
        /// </summary>
        public bool ForceReDetection { get; set; }

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

        /// <summary>
        /// 与鼠标事件关联的拾取轴。
        /// </summary>
        public Ax1 PickAxis
        {
            get
            {
                return Viewport.ViewAxis((int)ScreenPoint.X, (int)ScreenPoint.Y);
            }
        }

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

        /// <summary>
        /// 初始化 <see cref="MouseEventData"/> 类的新实例。
        /// </summary>
        public MouseEventData()
        {
            ScreenPoint = default;
            RawPoint = default;
            PointOnPlane = default;
            ForceReDetection = false;
        }

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

        /// <summary>
        /// 清除鼠标事件数据。
        /// </summary>
        public void Clear()
        {
            Viewport = default;
            ScreenPoint = default;
            RawPoint = default;
            PointOnPlane = default;
            ForceReDetection = false;
            DetectedAisInteractives.Clear();
            DetectedEntities.Clear();
            DetectedShapes.Clear();
        }

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

        /// <summary>
        /// 设置鼠标事件数据。
        /// </summary>
        public void Set(in Viewport viewport, in Point screenPoint, in Pnt rawPoint, in Pnt pointOnPlane, in InteractiveEntity detectedEntity,
                        in AIS_InteractiveObject detectedInteractive, in TopoDS_Shape detectedShape, in ModifierKeys modifierKeys)
        {
            Viewport = viewport;
            ScreenPoint = screenPoint;
            RawPoint = rawPoint;
            PointOnPlane = pointOnPlane;
            ModifierKeys = modifierKeys;

            DetectedAisInteractives.Clear();
            if (detectedInteractive != null)
                DetectedAisInteractives.Add(detectedInteractive);

            DetectedEntities.Clear();
            if (detectedEntity != null)
                DetectedEntities.Add(detectedEntity);

            DetectedShapes.Clear();
            if (detectedShape != null)
                DetectedShapes.Add(detectedShape);
        }
    }
}

上面的代码定义了一个接口 IMouseEventHandler 和一个类 MouseEventData,用于处理鼠标事件和存储鼠标事件的相关数据。这些类提供了对鼠标移动、按下和释放事件的处理,并提供了有关鼠标事件的详细信息,例如鼠标位置、点击的实体等。

18.

using System.Collections.Generic;
using System.Windows.Input;
using Macad.Presentation;

namespace Macad.Interaction
{
    /// <summary>
    /// 表示快捷键的作用范围。
    /// </summary>
    public enum ShortcutScope
    {
        Application,
        Workspace
    }

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

    /// <summary>
    /// 快捷键处理程序。
    /// </summary>
    public class ShortcutHandler
    {
        /// <summary>
        /// 表示一个快捷键。
        /// </summary>
        public class Shortcut
        {
            /// <summary>
            /// 获取或设置快捷键的键。
            /// </summary>
            public Key Key { get; }

            /// <summary>
            /// 获取或设置快捷键的修饰键。
            /// </summary>
            public ModifierKeys ModifierKeys { get; }

            /// <summary>
            /// 获取或设置与快捷键关联的命令。
            /// </summary>
            public ICommand Command { get; }

            /// <summary>
            /// 获取或设置传递给命令的参数。
            /// </summary>
            public object Parameter { get; }

            /// <summary>
            /// 使用指定的键、修饰键、命令和参数初始化 <see cref="Shortcut"/> 类的新实例。
            /// </summary>
            public Shortcut(Key key, ICommand command, object parameter = null)
                : this(key, ModifierKeys.None, command, parameter)
            {
            }

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

            /// <summary>
            /// 使用指定的键、修饰键、命令和参数初始化 <see cref="Shortcut"/> 类的新实例。
            /// </summary>
            public Shortcut(Key key, ModifierKeys modifierKeys, ICommand command, object parameter = null)
            {
                Key = key;
                ModifierKeys = modifierKeys;
                Command = command;
                Parameter = parameter;

                if (command is IActionCommand actionCommand)
                {
                    actionCommand.Shortcut = GetKeyString();
                }
            }

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

            /// <summary>
            /// 获取表示快捷键的字符串。
            /// </summary>
            public string GetKeyString()
            {
                if (ModifierKeys != ModifierKeys.None)
                {
                    return $"{ModifierKeys.ToString()} + {Key.ToString()}";
                }
                else
                {
                    return Key.ToString();
                }
            }
        }

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

        readonly Dictionary<ShortcutScope, List<Shortcut>> _ShortcutScopes = new();

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

        /// <summary>
        /// 初始化 <see cref="ShortcutHandler"/> 类的新实例。
        /// </summary>
        public ShortcutHandler()
        {
            // TODO: 从外部配置文件中读取
            _ShortcutScopes.Add(ShortcutScope.Application, new List<Shortcut>
            {
                new(Key.Y, ModifierKeys.Control, WorkspaceCommands.DoRedo),
                new(Key.Z, ModifierKeys.Control, WorkspaceCommands.DoUndo),
                new(Key.C, ModifierKeys.Control, WorkspaceCommands.CopyToClipboard),
                new(Key.X, ModifierKeys.Control, WorkspaceCommands.CutToClipboard),
                new(Key.V, ModifierKeys.Control, WorkspaceCommands.PasteFromClipboard),
            });

            _ShortcutScopes.Add(ShortcutScope.Workspace, new List<Shortcut>
            {
                new(Key.G, WorkspaceCommands.ToggleGrid),
                new(Key.S, WorkspaceCommands.ToggleSnappingEnabled),
                new(Key.F, WorkspaceCommands.ZoomFitSelected),
                new(Key.F, ModifierKeys.Control, WorkspaceCommands.ZoomFitAll),
                new(Key.T, WorkspaceCommands.Transform),
                new(Key.W, WorkspaceCommands.AlignWorkingPlane),
                new(Key.Delete, WorkspaceCommands.DeleteEntity),
                new(Key.D, ModifierKeys.Control, WorkspaceCommands.DuplicateEntity),
                new(Key.R, ModifierKeys.Control, ModelCommands.CreateReference),
                new(Key.I, WorkspaceCommands.ToggleIsolateSelection),
                new(Key.E, WorkspaceCommands.StartEditing),
                new(Key.Escape, WorkspaceCommands.Escape),
            });

            // 注册
        }

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

        /// <summary>
        /// 添加快捷键。
        /// </summary>
        public void AddShortcut(ShortcutScope scope, Shortcut shortcut)
        {
            if (!_ShortcutScopes.TryGetValue(scope, out var shortcutList))
            {
                shortcutList = new List<Shortcut>();
                _ShortcutScopes.Add(scope, shortcutList);
            }
            shortcutList.Add(shortcut);
        }

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

        /// <summary>
        /// 检查是否按下指定的快捷键,并执行相关命令。
        /// </summary>
        public bool KeyPressed(ShortcutScope scope, Key key, ModifierKeys modifierKeys)
        {
            if (!_ShortcutScopes.ContainsKey(scope))
                return false;

            var shortcut = _ShortcutScopes[scope].Find(s => s.Key == key && s.ModifierKeys == modifierKeys);
            if (shortcut?.Command == null || !shortcut.Command.CanExecute(shortcut.Parameter))
                return false;

            shortcut.Command.Execute(shortcut.Parameter);
            return true;
        }

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

        /// <summary>
        /// 获取与指定命令关联的快捷键。
        /// </summary>
        public string GetKeyString(ICommand command)
        {
            foreach (var shortcutScope in _ShortcutScopes)
            {
                foreach (var shortcut in shortcutScope.Value)
                {
                    if (shortcut.Command.Equals(command))
                        return shortcut.GetKeyString();
                }
            }

            return null;
        }
    }
}

上面的代码定义了一个 ShortcutHandler 类,用于处理快捷键及其相关操作。该类中包含了一个内部类 Shortcut,用于表示快捷键的信息,包括键、修饰键、关联的命令和参数。ShortcutHandler 类提供了添加快捷键、检查是否按下指定的快捷键并执行相关命令、以及获取与指定命令关联的快捷键等功能。

19.

using System;
using Macad.Common;
using Macad.Core;
using Macad.Occt;

namespace Macad.Interaction
{
    /// <summary>
    /// 表示吸附模式。
    /// </summary>
    [Flags]
    public enum SnapMode
    {
        None = 0,
        Grid = 1 << 0,
        Vertex = 1 << 1,
        Edge = 1 << 2,
        Face = 1 << 3
    }

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

    /// <summary>
    /// 表示吸附信息。
    /// </summary>
    public class SnapInfo
    {
        /// <summary>
        /// 获取或设置吸附模式。
        /// </summary>
        public SnapMode SnapMode { get; set; }

        /// <summary>
        /// 获取或设置吸附点。
        /// </summary>
        public Pnt Point { get; set; }
    }

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

    /// <summary>
    /// 表示二维吸附信息。
    /// </summary>
    public class SnapInfo2D
    {
        /// <summary>
        /// 获取或设置吸附模式。
        /// </summary>
        public SnapMode SnapMode { get; set; }

        /// <summary>
        /// 获取或设置吸附点。
        /// </summary>
        public Pnt2d Point { get; set; }

        /// <summary>
        /// 获取或设置距离。
        /// </summary>
        public double Distance { get; set; }
    }

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

    /// <summary>
    /// 表示吸附处理程序。
    /// </summary>
    public sealed class SnapHandler : BaseObject, IDisposable
    {
        /// <summary>
        /// 获取或设置支持的吸附模式。
        /// </summary>
        public SnapMode SupportedSnapModes
        {
            get { return _SupportedSnapModes; }
            private set
            {
                _SupportedSnapModes = value; 
                InteractiveContext.Current?.WorkspaceController?.Selection?.Invalidate();
            }
        }

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

        SnapMode _SupportedSnapModes = SnapMode.None;
        WorkspaceController _WorkspaceController;

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

        /// <summary>
        /// 初始化 <see cref="SnapHandler"/> 类的新实例。
        /// </summary>
        public SnapHandler(WorkspaceController workspaceController)
        {
            _WorkspaceController = workspaceController;
            Tool.ToolActionChanged += _Tool_ToolActionChanged;
        }

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

        /// <summary>
        /// 释放资源。
        /// </summary>
        public void Dispose()
        {
            Tool.ToolActionChanged -= _Tool_ToolActionChanged;
            _WorkspaceController = null;
        }

        //--------------------------------------------------------------------------------------------------
        
        void _Tool_ToolActionChanged(Tool sender, ToolAction action)
        {
            SupportedSnapModes = action?.SupportedSnapModes ?? SnapMode.None;
        }

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

        /// <summary>
        /// 吸附。
        /// </summary>
        public SnapInfo Snap(MouseEventData mouseEvent)
        {
            if (!InteractiveContext.Current.EditorState.SnappingEnabled)
            {
                return null;
            }

            SnapInfo info = null;

            if (mouseEvent.DetectedShapes.Count == 1)
            {
                var detectedShape = mouseEvent.DetectedShapes[0];
                if (SupportedSnapModes.HasFlag(SnapMode.Vertex) 
                    && InteractiveContext.Current.EditorState.SnapToVertexSelected 
                    && (detectedShape.ShapeType() == TopAbs_ShapeEnum.VERTEX))
                {
                    // On Vertex
                    var vertex = TopoDS.Vertex(detectedShape);
                    info = new SnapInfo()
                    {
                        Point = BRep_Tool.Pnt(vertex),
                        SnapMode = SnapMode.Vertex
                    };
                }
                else if (SupportedSnapModes.HasFlag(SnapMode.Edge)
                    && InteractiveContext.Current.EditorState.SnapToEdgeSelected
                    && (detectedShape.ShapeType() == TopAbs_ShapeEnum.EDGE))
                {
                    // On Edge
                    var edge = TopoDS.Edge(detectedShape);
                    double umin = 0, umax = 0;
                    var curve = BRep_Tool.Curve(edge, ref umin, ref umax);
                    if (curve != null)
                    {
                        var extrema = new GeomAPI_ExtremaCurveCurve(curve,
                                                                    new Geom_Line(_WorkspaceController.ActiveViewport.ViewAxis(Convert.ToInt32(mouseEvent.ScreenPoint.X), Convert.ToInt32(mouseEvent.ScreenPoint.Y))));
                        if (extrema.NbExtrema() >= 1)
                        {
                            Pnt p1 = new Pnt();
                            Pnt p2 = new Pnt();
                            if (extrema.TotalNearestPoints(ref p1, ref p2))
                            {
                                info = new SnapInfo()
                                {
                                    Point = p1,
                                    SnapMode = SnapMode.Edge
                                };
                            }
                        }
                    }
                }
            }
            else if (mouseEvent.DetectedAisInteractives.Count == 1)
            {
                if (SupportedSnapModes.HasFlag(SnapMode.Vertex) 
                    && InteractiveContext.Current.EditorState.SnapToVertexSelected 
                    && (mouseEvent.DetectedAisInteractives[0] is AIS_Point aisPoint))
                {
                    // On Vertex
                    info = new SnapInfo()
                    {
                        Point = aisPoint.Component().Pnt(),
                        SnapMode = SnapMode.Vertex
                    };
                }
            }
            else if (SupportedSnapModes.HasFlag(SnapMode.Grid)
                && InteractiveContext.Current.EditorState.SnapToGridSelected
                && _WorkspaceController.Workspace.GridEnabled)
            {
                if (_WorkspaceController.Workspace.ProjectToGrid(_WorkspaceController.ActiveViewport,
                                                                 Convert.ToInt32(mouseEvent.ScreenPoint.X),
                                                                 Convert.ToInt32(mouseEvent.ScreenPoint.Y),
                                                                 out Pnt gridPnt))
                {
                    // On Grid
                    info = new SnapInfo()
                    {
                        Point = gridPnt,
                        SnapMode = SnapMode.Grid
                    };
                }
            }

            if (info != null)
            {
                _WorkspaceController.CursorPosition = info.Point;
            }
            return info;
        }

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

        /// <summary>
        /// 在平面上进行吸附。
        /// </summary>
        public Pnt2d? SnapOnPlane(SnapInfo snapInfo, Pln? localPlane = null)
        {
            if (snapInfo != null)
            {
                var plane = localPlane ?? _WorkspaceController.Workspace.WorkingPlane;
                switch (snapInfo.SnapMode)
                {
                    case SnapMode.Grid:
                    case SnapMode.Vertex:
                    case SnapMode.Edge:
                        return ProjLib.Project(plane, snapInfo.Point);
                }
            }
            return null;
        }

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

        /// <summary>
        /// 是否需要激活子形状。
        /// </summary>
        public bool NeedActiveSubshapes(SubshapeType subshapeType)
        {
            switch (subshapeType)
            {
                case SubshapeType.Vertex:
                    return InteractiveContext.Current.EditorState.SnappingEnabled
                           && InteractiveContext.Current.EditorState.SnapToVertexSelected
                           && SupportedSnapModes.HasFlag(SnapMode.Vertex);

                case SubshapeType.Edge:
                    return InteractiveContext.Current.EditorState.SnappingEnabled
                           && InteractiveContext.Current.EditorState.SnapToEdgeSelected
                           && SupportedSnapModes.HasFlag(SnapMode.Edge);
            }
            return false;
        }
    }
}

上面的代码定义了一个 SnapHandler 类,用于处理吸附操作。该类中包含了表示吸附模式的枚举 SnapMode,以及表示吸附信息的类 SnapInfoSnapInfo2DSnapHandler 类提供了吸附操作的实现,包括吸附计算、在平面上进行吸附、以及判断是否需要激活子形状等功能。

20.

using System;  // 引入 System 命名空间
using System.Diagnostics;  // 引入 System.Diagnostics 命名空间
using System.Runtime.InteropServices;  // 引入 System.Runtime.InteropServices 命名空间
using System.Windows;  // 引入 System.Windows 命名空间
using System.Windows.Input;  // 引入 System.Windows.Input 命名空间
using System.Windows.Interop;  // 引入 System.Windows.Interop 命名空间
using Macad.Common;  // 引入 Macad.Common 命名空间
using Macad.Common.Interop;  // 引入 Macad.Common.Interop 命名空间
using Macad.Core;  // 引入 Macad.Core 命名空间

namespace Macad.Interaction  // 定义 Macad.Interaction 命名空间
{
    /// <summary>
    /// 空间导航器参数集。
    /// </summary>
    public sealed class SpaceNavigatorParameterSet : OverridableParameterSet  // 定义 SpaceNavigatorParameterSet 类,继承自 OverridableParameterSet
    {
        /// <summary>
        /// 获取或设置移动灵敏度。
        /// </summary>
        public double 移动灵敏度 { get => GetValue<double>(); set => SetValue(value); }  // 定义移动灵敏度属性

        /// <summary>
        /// 获取或设置旋转灵敏度。
        /// </summary>
        public double 旋转灵敏度 { get => GetValue<double>(); set => SetValue(value); }  // 定义旋转灵敏度属性

        /// <summary>
        /// 获取或设置缩放灵敏度。
        /// </summary>
        public double 缩放灵敏度 { get => GetValue<double>(); set => SetValue(value); }  // 定义缩放灵敏度属性

        /// <summary>
        /// 获取或设置滚动死区。
        /// </summary>
        public double 滚动死区 { get => GetValue<double>(); set => SetValue(value); }  // 定义滚动死区属性

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

        /// <summary>
        /// 初始化 <see cref="SpaceNavigatorParameterSet"/> 类的新实例。
        /// </summary>
        public SpaceNavigatorParameterSet()  // 定义构造函数
        {
            SetDefaultValue(nameof(移动灵敏度), 1.0);  // 设置默认值
            SetDefaultValue(nameof(旋转灵敏度), 1.0);  // 设置默认值
            SetDefaultValue(nameof(缩放灵敏度), 1.0);  // 设置默认值
            SetDefaultValue(nameof(滚动死区), 1.25);  // 设置默认值
        }
    }

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

    /// <summary>
    /// 空间导航器。
    /// </summary>
    public class SpaceNavigator  // 定义 SpaceNavigator 类
    {
        static double _MoveDataDivisorScale = 16.0;  // 定义移动数据除数比例
        static double _RotateDataDivisorScale = 400.0;  // 定义旋转数据除数比例
        static double _ZoomDataDivisorScale = 20480.0;  // 定义缩放数据除数比例

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

        bool _Initialized = false;  // 定义初始化状态变量
        uint _WindowMessageCode;  // 定义窗口消息代码变量
        IntPtr _DeviceHandle;  // 定义设备句柄变量

        static double _MoveDataDivisor = _MoveDataDivisorScale;  // 定义移动数据除数变量
        static double _RotateDataDivisor = _RotateDataDivisorScale;  // 定义旋转数据除数变量
        static double _ZoomDataDivisor = _ZoomDataDivisorScale;  // 定义缩放数据除数变量
        static double _RollDeadZone = 1.25;  // 定义滚动死区变量

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

        /// <summary>
        /// 初始化空间导航器。
        /// </summary>
        public bool Init(Window targetWindow)  // 定义初始化方法
        {
            try  // 尝试执行以下代码块
            {
                var result = Driver.SiInitialize();  // 调用驱动程序初始化方法
                if (result != Driver.SpwRetVal.SPW_NO_ERROR)  // 如果初始化结果不是无错误
                {
                    var message = Marshal.PtrToStringAnsi(Driver.SpwErrorString(result));  // 获取错误消息
                    Messages.Info($"[SpaceNavigator] Driver not initialized: {message}");  // 输出消息
                    return false;  // 返回初始化失败
                }

                _WindowMessageCode = Win32Api.RegisterWindowMessage("SpaceWareMessage00");  // 注册窗口消息
                IntPtr windowHandle = new WindowInteropHelper(targetWindow).Handle;  // 获取窗口句柄
                Driver.SiOpenData openData = new();  // 创建 SiOpenData 实例
                Driver.SiOpenWinInit(ref openData, windowHandle);  // 初始化打开数据
                
                _DeviceHandle = Driver.SiOpen("Macad3D", -1 /* and device */, IntPtr.Zero, 1 /* Event */, ref openData);  // 打开设备
                if (_DeviceHandle == IntPtr.Zero)  // 如果设备句柄为空
                {
                    Messages.Warning("[SpaceNavigator] Device could not be opened.");  // 输出警告消息
                    return false;  // 返回初始化失败
                }
                
                var hwndSource = HwndSource.FromHwnd(windowHandle);  // 获取窗口源
                if (hwndSource == null)  // 如果窗口源为空
                {
                    Console.WriteLine("[SpaceNavigator] Window hook not successful.");  // 输出消息
                    return false;  // 返回初始化失败
                }
                hwndSource.AddHook(_WndProc);  // 添加窗口过程钩子

                _Initialized = true;  // 设置初始化状态为真
                _UpdateParameters();  // 更新参数

                return true;  // 返回初始化成功
            }
            catch (DllNotFoundException)  // 捕获找不到 DLL 异常
            {
                Console.WriteLine("[SpaceNavigator] Device driver dll not found (siapp.dll).");  // 输出消息
                return false;  // 返回初始化失败
            }
            catch (Exception)  // 捕获所有其他异常
            {
                return false;  // 返回初始化失败
            }
        }

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

        static void _UpdateParameters()  // 定义更新参数方法
        {
            Debug.Assert(InteractiveContext.Current != null);  // 断言当前交互上下文不为空

            // 初始化参数
            var paramSet = InteractiveContext.Current.Parameters.Get<SpaceNavigatorParameterSet>();  // 获取参数集
            _MoveDataDivisor = _MoveDataDivisorScale * paramSet.MoveSensitivity;  // 更新移动数据除数
            _RotateDataDivisor = _RotateDataDivisorScale * paramSet.RotationSensitivity;  // 更新旋转数据除数
            _ZoomDataDivisor = _ZoomDataDivisorScale * paramSet.ZoomSensitivity;  // 更新缩放数据除数
            _RollDeadZone = paramSet.RollDeadZone;  // 更新滚动死区
        }

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

        IntPtr _WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)  // 定义窗口过程方法
        {
            if ((uint) msg == _WindowMessageCode)  // 如果消息代码与注册的窗口消息代码相等
            {
                var viewportController = InteractiveContext.Current.ViewportController;  // 获取视口控制器
                if (viewportController != null)  // 如果视口控制器不为空
                {
                    Driver.SiGetEventData eventData = new();  // 创建 SiGetEventData 实例
                    Driver.SiSpwEvent evt = new();  // 创建 SiSpwEvent 实例
                    Driver.SiGetEventWinInit(ref eventData, _WindowMessageCode, wparam, lparam);  // 初始化事件数据
                    if (Driver.SiGetEvent(_DeviceHandle, 0, ref eventData, ref evt) == Driver.SpwRetVal.SI_IS_EVENT  // 如果获取事件成功且事件类型为运动事件
                        && evt.Type == Driver.SiEventType.SI_MOTION_EVENT)
                    {
                        double panX = -evt.Tx / (_MoveDataDivisor * viewportController.Viewport.Scale);  // 计算平移 X
                        double panY = -evt.Ty / (_MoveDataDivisor * viewportController.Viewport.Scale);  // 计算平移 Y
                        viewportController.Pan(panX, panY);  // 执行平移操作

                        double zoom = evt.Tz / _ZoomDataDivisor;  // 计算缩放
                        viewportController.Zoom(zoom);  // 执行缩放操作

                        double rotX = evt.Rx / _RotateDataDivisor;  // 计算旋转 X
                        double rotY = evt.Ry / _RotateDataDivisor;  // 计算旋转 Y
                        double rotZ = -evt.Rz / _RotateDataDivisor;  // 计算旋转 Z

                        // 添加滚动死区到旋转
                        if (Math.Abs(rotZ) < _RollDeadZone)  // 如果旋转 Z 的绝对值小于滚动死区
                        {
                            rotZ = 0.0;  // 设置旋转 Z 为 0
                        }
                        else  // 否则
                        {
                            rotZ -= rotZ > 0 ? _RollDeadZone : -_RollDeadZone;  // 对旋转 Z 进行滚动死区处理
                        }

                        viewportController.Rotate(rotY, rotX, rotZ);  // 执行旋转操作
                        viewportController.MouseMove(Keyboard.Modifiers);  // 执行鼠标移动操作
                    }
                }

                handled = true;  // 标记消息已处理
            }
            return IntPtr.Zero;  // 返回零指针
        }

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

        public void DeInit()  // 定义反初始化方法
        {
            if (_Initialized)  // 如果已初始化
            {
                Driver.SiTerminate();  // 终止驱动程序
                _Initialized = false;  // 设置初始化状态为假
            }
        }

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

        static SpaceNavigator()  // 静态构造函数
        {
            SpaceNavigatorParameterSet.ParameterChanged += (set, key) => _UpdateParameters();  // 注册参数变更事件处理程序
        }

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

        static class Driver  // 定义驱动程序类
        {
            internal enum SpwRetVal  // 定义返回值枚举
            {
                SPW_NO_ERROR = 0,  // 无错误
                SI_IS_EVENT = 5  // 是事件
            }

            internal enum SiEventType  // 定义事件类型枚举
            {
                SI_MOTION_EVENT = 2  // 运动事件
            }

            [StructLayout(LayoutKind.Sequential, Size = 284)]  // 结构布局
            internal struct SiOpenData  // 定义 SiOpenData 结构体
            {
            }

            [StructLayout(LayoutKind.Sequential, Size = 20)]  // 结构布局
            internal struct SiGetEventData  // 定义 SiGetEventData 结构体
            {
            }

            [StructLayout(LayoutKind.Sequential, Size = 5124)]  // 结构布局
            internal struct SiSpwEvent  // 定义 SiSpwEvent 结构体
            {
                internal SiEventType Type;  // 事件类型
                internal uint BtnLast;  // 上次按钮
                internal uint BtnCurrent;  // 当前按钮
                internal uint BtnPressed;  // 按下按钮
                internal uint BtnReleased;  // 释放按钮
                internal int Tx;  // X 位移
                internal int Ty;  // Y 位移
                internal int Tz;  // Z 位移
                internal int Rx;  // X 旋转
                internal int Ry;  // Y 旋转
                internal int Rz;  // Z 旋转
                internal int Period;  // 周期
            }

            [DllImport("siappdll")]  // 引入外部 DLL
            internal static extern SpwRetVal SiInitialize();  // 定义驱动程序初始化方法

            [DllImport("siappdll")]  // 引入外部 DLL
            internal static extern void SiTerminate();  // 定义驱动程序终止方法

            [DllImport("siappdll")]  // 引入外部 DLL
            internal static extern IntPtr SpwErrorString(SpwRetVal code);  // 定义错误字符串获取方法

            [DllImport("siappdll")]  // 引入外部 DLL
            internal static extern void SiOpenWinInit(ref SiOpenData openData, IntPtr windowHandle);  // 定义窗口初始化方法

            [DllImport("siappdll", CharSet = CharSet.Ansi)]  // 引入外部 DLL
            internal static extern IntPtr SiOpen(string appName, int deviceId, IntPtr typeMask, int mode, ref SiOpenData openData);  // 定义打开方法

            [DllImport("siappdll")]  // 引入外部 DLL
            internal static extern IntPtr SiGetEventWinInit(ref SiGetEventData eventData, uint msg, IntPtr wparam, IntPtr lparam);  // 定义事件初始化方法

            [DllImport("siappdll")]  // 引入外部 DLL
            internal static extern SpwRetVal SiGetEvent(IntPtr deviceHandle, int flags, ref SiGetEventData eventData, ref SiSpwEvent spwEvent);  // 定义获取事件方法
        }
    }
}

在这段代码中,我们定义了一个名为SpaceNavigator的类,它用于处理空间导航器的输入。同时,还定义了一个名为SpaceNavigatorParameterSet的类,用于存储空间导航器的参数。整段代码中包含了初始化、更新参数、处理窗口消息等功能。

21.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
using Macad.Common;
using Macad.Common.Interop;
using Macad.Core;
using Macad.Occt;
using Macad.Occt.Helper;
using Macad.Resources;
using Point = System.Windows.Point;

namespace Macad.Interaction
{
    public sealed class ViewportController : BaseObject, IDisposable
    {
        const int RubberbandFreehandSelectionThresholdSquared = 100;

        #region Enums

        public enum RubberbandSelectionMode
        {
            Rectangle,
            Freehand
        }

        #endregion

        #region Properties

        public WorkspaceController WorkspaceController { get; private set; }

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

        public Viewport Viewport { get; private set; }

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

        public bool LockedToPlane
        {
            get { return _LockedToPlane; }
            set
            {
                if (_LockedToPlane != value)
                {
                    if (value)
                    {
                        SetPredefinedView(PredefinedViews.WorkingPlane);
                        
                    }
                    _LockedToPlane = value;
                    _SetViewCube(!value);
                    _SetTrihedron(!value && _ShowTrihedron);
                    RaisePropertyChanged();
                }
            }
        }

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

        public bool IsInRubberbandSelection
        {
            get { return _AisRubberBand != null; }
        }

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

        #endregion

        #region Member variables
        
        static WNT_WClass _OcWindowClass;
        const double _OrbitProjectionConstraint = Maths.HalfPI - 0.000000000001;
        WNT_Window _OcWindow;
        bool _ZoomFitAllOnInit;
        
        MouseMoveMode _CurrentMouseMoveMode;
        Point _StartedMousePosition;
        Point _LastMousePosition;
        Pnt? _GravityPoint;
        bool _LockedToPlane;
        bool _ShowTrihedron;

        AIS_RubberBand _AisRubberBand;
        RubberbandSelectionMode _RubberbandMode;
        bool _RubberbandIncludeTouched;
        readonly List<ValueTuple<int, int>> _RubberbandPoints = new();

        Macad.Occt.Ext.AIS_ViewCubeEx _AisViewCube;

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

        #endregion

        #region Initialization

        public ViewportController(Viewport viewport, WorkspaceController workspaceController)
        {
            Debug.Assert(viewport != null);

            Viewport = viewport;
            WorkspaceController = workspaceController;

            Init();
        }

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

        ~ViewportController()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        void Dispose(bool disposing)
        {
            ViewportParameterSet.ParameterChanged -= _ViewportParameterSet_ParameterChanged;

            _AisViewCube?.Dispose();
            _AisViewCube = null;

            _AisRubberBand?.Dispose();
            _AisRubberBand = null;

            Viewport.Dispose();
            if (_OcWindow != null && !_OcWindow.IsDisposed())
            {
                if (_OcWindow.IsMapped())
                    _OcWindow.Unmap();
                _OcWindow.Dispose();
                _OcWindow = null;
            }
        }

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

        void Init()
        {
            ViewportParameterSet.ParameterChanged += _ViewportParameterSet_ParameterChanged;

            var parameterSet = InteractiveContext.Current.Parameters.Get<ViewportParameterSet>();
            Viewport.Init(parameterSet.EnableAntialiasing);
        }

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

        public IntPtr InitWindow(IntPtr parentHWnd, Int32Rect initialRect)
        {
            Debug.Assert(Viewport.V3dView != null);

            uint style;

            if (_OcWindowClass == null)
            {
                style = Win32Api.CS_OWNDC;
                _OcWindowClass = new WNT_WClass(new TCollection_AsciiString("WorkspaceView"), IntPtr.Zero, style, 0);
            }

            if (initialRect.IsEmpty)
            {
                initialRect = new Int32Rect(0, 0, 64, 64);
            }

            style = Win32Api.WS_VISIBLE | (parentHWnd == IntPtr.Zero ? Win32Api.WS_POPUP : Win32Api.WS_CHILD);
            _OcWindow = new WNT_Window("WorkspaceView", _OcWindowClass, style, initialRect.X, initialRect.Y, initialRect.Width, initialRect.Height, Quantity_NameOfColor.GRAY50, parentHWnd);
            _OcWindow.Map();

            Viewport.V3dView.SetWindow(_OcWindow);
            //Viewport.InitV3dView();
            if (_ZoomFitAllOnInit)
            {
                _ZoomFitAllOnInit = false;
                ZoomFitAll();
            }
            Viewport.V3dView.Update();
            Viewport.V3dView.MustBeResized();
            Viewport.V3dView.SetImmediateUpdate(false);

            _UpdateParameter();

            var handle = _OcWindow.HWindow();
            return handle;
        }

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

        void _ViewportParameterSet_ParameterChanged(OverridableParameterSet set, string key)
        {
            _UpdateParameter();
        }

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

        void _UpdateParameter()
        {
            var parameterSet = InteractiveContext.Current.Parameters.Get<ViewportParameterSet>();
            _SetViewCube(parameterSet.ShowViewCube, parameterSet.ViewCubeSize, parameterSet.ViewCubeAnimationDuration);
            _SetTrihedron(parameterSet.ShowTrihedron);
            _ShowTrihedron = parameterSet.ShowTrihedron;
        }
        
        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Viewport navigation

        public enum PredefinedViews
        {
            Top,
            Bottom,
            Left,
            Right,
            Front,
            Back,
            WorkingPlane
        }

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

        public void SetPredefinedView(PredefinedViews predefinedView)
        {
            if (predefinedView == PredefinedViews.WorkingPlane)
            {
                var plane = WorkspaceController.Workspace.WorkingPlane;
                var dir = plane.Axis.Direction;
                Viewport.V3dView.SetProj(dir.X, dir.Y, dir.Z);
                var up = plane.YAxis.Direction;
                Viewport.V3dView.SetUp(up.X, up.Y, up.Z);
                return;
            }

            V3d_TypeOfOrientation orientation;
            switch (predefinedView)
            {
                case PredefinedViews.Top:
                    orientation = V3d_TypeOfOrientation.Zup_Top;
                    break;
                case PredefinedViews.Bottom:
                    orientation = V3d_TypeOfOrientation.Zup_Bottom;
                    break;
                case PredefinedViews.Left:
                    orientation = V3d_TypeOfOrientation.Zup_Left;
                    break;
                case PredefinedViews.Right:
                    orientation = V3d_TypeOfOrientation.Zup_Right;
                    break;
                case PredefinedViews.Front:
                    orientation = V3d_TypeOfOrientation.Zup_Front;
                    break;
                case PredefinedViews.Back:
                    orientation = V3d_TypeOfOrientation.Zup_Back;
                    break;
                default:
                    return;
            }

            var viewCubeOwner = new AIS_ViewCubeOwner(_AisViewCube, orientation);
            _AisViewCube.HandleClick(viewCubeOwner);
            viewCubeOwner.Dispose();

            WorkspaceController.Invalidate();
        }

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

        public enum MouseMoveMode
        {
            None,
            Panning,
            Rotating,
            Twisting,
            Zooming
        }

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

        public void MouseMove(Point pos, ModifierKeys modifierKeys = ModifierKeys.None, MouseMoveMode mode = MouseMoveMode.None)
        {
            if (IsInRubberbandSelection)
            {
                _LastMousePosition = pos;
                _UpdateRubberbandSelection();
                WorkspaceController.Invalidate(true);
                return;
            }

            if (_CurrentMouseMoveMode != mode)
            {
                if (mode == MouseMoveMode.None)
                {
                    _ResetMouseMoveMode();
                }
                else
                {
                    _StartedMousePosition = pos;
                    _SetMouseMoveMode(mode);
                }
            }

            switch (_CurrentMouseMoveMode)
            {
                case MouseMoveMode.Panning:
                    Viewport.V3dView.Pan((int) (pos.X - _LastMousePosition.X), -(int) (pos.Y - _LastMousePosition.Y), 1.0, true);
                    Viewport.OnViewMoved();
                    break;

                case MouseMoveMode.Twisting:
                    Rotate(0, 0, (pos.Y - _LastMousePosition.Y) / 12.0);
                    break;

                case MouseMoveMode.Rotating:
                    // Turntable
                    Rotate((_LastMousePosition.X - pos.X) / 6.0, (_LastMousePosition.Y - pos.Y) / 6.0, 0);
                    break;

                case MouseMoveMode.Zooming:
                    Viewport.V3dView.ZoomAtPoint((int) _LastMousePosition.X, (int) pos.Y, (int) pos.X, (int) _LastMousePosition.Y);
                    Viewport.OnViewMoved();
                    break;
            }

            WorkspaceController.MouseMove(this, pos, modifierKeys);
            WorkspaceController.Invalidate();

            _LastMousePosition = pos;
        }
        
        //--------------------------------------------------------------------------------------------------

        public void MouseMove(ModifierKeys modifierKeys = ModifierKeys.None)
        {
            if (_AisRubberBand != null)
                return;

            WorkspaceController.MouseMove(this, _LastMousePosition, modifierKeys);
        }

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

        public void MouseDown(ModifierKeys modifierKeys = ModifierKeys.None)
        {
            WorkspaceController.MouseDown(this, modifierKeys);
        }

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

        public void MouseUp(ModifierKeys modifierKeys = ModifierKeys.None)
        {
            if (IsInRubberbandSelection)
            {
                _StopRubberbandSelection();
            }
            WorkspaceController.MouseUp(this, modifierKeys);
        }

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

        void _SetMouseMoveMode(MouseMoveMode mode)
        {
            switch (mode)
            {
                case MouseMoveMode.Panning:
                    _CurrentMouseMoveMode = MouseMoveMode.Panning;
                    break;

                case MouseMoveMode.Rotating:
                    _CurrentMouseMoveMode = MouseMoveMode.Rotating;
                    _GravityPoint ??= Viewport.V3dView.GravityPoint();
                    break;

                case MouseMoveMode.Twisting:
                    _CurrentMouseMoveMode = MouseMoveMode.Twisting;
                    break;

                case MouseMoveMode.Zooming:
                    Viewport.V3dView.StartZoomAtPoint((int) (_StartedMousePosition.X), (int) (_StartedMousePosition.Y));
                    _CurrentMouseMoveMode = MouseMoveMode.Zooming;
                    break;
            }
        }

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

        void _ResetMouseMoveMode()
        {
            _GravityPoint = null;
            _CurrentMouseMoveMode = MouseMoveMode.None;
        }

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

        public void Rotate(double yawDeg, double pitchDeg, double rollDeg)
        {
            if (!_LockedToPlane)
            {
                if (Math.Abs(yawDeg) > 0.001 || Math.Abs(pitchDeg) > 0.001)
                {
                    if (Viewport.Twist == 180)
                    {
                        Viewport.Twist = 0;
                    }
                    
                    var pitch = pitchDeg.ToRad();
                    var yaw = yawDeg.ToRad();

                    // Constraint polar regions, do not go 'overhead'
                    var upDir = Viewport.GetUpDirection();
                    var viewDir = Viewport.GetViewDirection();
                    var angleLeft = _OrbitProjectionConstraint - Ax2.XOY.Angle(new Ax2(Pnt.Origin, upDir));
                    if (viewDir.Z < 0 && pitch < -angleLeft)
                    {
                        pitch = -angleLeft;
                    }
                    else if (viewDir.Z > 0 && pitch > angleLeft)
                    {
                        pitch = angleLeft;
                    }
                
                    var gravityPoint = _GravityPoint ?? Viewport.V3dView.GravityPoint();
                    Trsf trsf1 = new Trsf(new Ax1(gravityPoint, Viewport.GetRightDirection()), pitch);
                    Viewport.V3dView.Camera().Transform(trsf1);
                    Trsf trsf2 = new Trsf(new Ax1(gravityPoint, Dir.DZ), yaw);
                    Viewport.V3dView.Camera().Transform(trsf2);
                }

                if (Math.Abs(rollDeg) > 0.001)
                {
                    Viewport.V3dView.Turn(V3d_TypeOfAxe.Z, rollDeg.ToRad(), true);
                }
            }

            WorkspaceController.Invalidate();
            Viewport.OnViewMoved();
        }

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

        public void Pan(double dX, double dY)
        {
            Viewport.V3dView.Panning(dX, dY, 1.0, true);
            WorkspaceController.Invalidate();
            Viewport.OnViewMoved();
        }

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

        public void Zoom(Point pos, double value)
        {
            double delta = value * 20.0;
            if (_CurrentMouseMoveMode != MouseMoveMode.Zooming)
            {
                Viewport.V3dView.StartZoomAtPoint((int) pos.X, (int) (pos.Y - delta));
            }

            Viewport.V3dView.ZoomAtPoint((int) pos.X, (int) (pos.Y - delta), (int) pos.X, (int) (pos.Y + delta));
            WorkspaceController.Invalidate();
            Viewport.OnViewMoved();
        }

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

        public void Zoom(double value)
        {
            if (value > 0)
            {
                Viewport.V3dView.SetZoom(1.0 + value, true);
            }
            else if (value < 0)
            {
                Viewport.V3dView.SetZoom(1.0 / (1.0-value), true);
            }
            WorkspaceController.Invalidate();
            Viewport.OnViewMoved();
        }

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

        public void ZoomFitAll()
        {
            if (_OcWindow == null)
            {
                // We need a window, defer call
                _ZoomFitAllOnInit = true;
                return;
            }
            WorkspaceController.VisualObjects.UpdateInvalidatedEntities();
            Viewport.V3dView.FitAll(0.1, false);
            Viewport.V3dView.ZFitAll(1.0);
            WorkspaceController.Invalidate();
            Viewport.OnViewMoved();
        }

        //--------------------------------------------------------------------------------------------------
        
        public void ZoomFitSelected()
        {
            WorkspaceController.VisualObjects.UpdateInvalidatedEntities();
            WorkspaceController.Workspace.AisContext.FitSelected(Viewport.V3dView, 0.1, false);
            WorkspaceController.Invalidate();
            Viewport.OnViewMoved();
        }

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

        #endregion

        #region Usability Tools

        void _SetViewCube(bool isVisible)
        {
            var aisContext = WorkspaceController.Workspace.AisContext;

            if (_AisViewCube == null)
                return;

            if (isVisible && !aisContext.IsDisplayed(_AisViewCube))
            {
                aisContext.Display(_AisViewCube, false);
                WorkspaceController.Invalidate(true);
            }
            else if (!isVisible && aisContext.IsDisplayed(_AisViewCube))
            {
                aisContext.Remove(_AisViewCube, false);
                WorkspaceController.Invalidate(true);
            }
        }

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

        void _SetViewCube(bool isVisible, uint size, double duration)
        {
            var aisContext = WorkspaceController.Workspace.AisContext;
            
            if (_AisViewCube != null)
            {
                _SetViewCube(isVisible);
                return;
            }

            if (!isVisible)
                return;

            var bitmap = ResourceUtils.ReadBitmapFromResource(@"Visual\ViewCubeSides.png");
            if (bitmap == null)
            {
                Messages.Error($"Could not load view cube texture from resource.");
                return;
            }

            var pixmap = PixMapHelper.ConvertFromBitmap(bitmap);
            if (pixmap == null)
            {
                Messages.Error($"Could not load view cube texture into pixmap.");
                return;
            }

            _AisViewCube = new Macad.Occt.Ext.AIS_ViewCubeEx();
            _AisViewCube.SetSize(size * Viewport.DpiScale);
            _AisViewCube.SetBoxFacetExtension(size * Viewport.DpiScale * 0.15);
            _AisViewCube.SetViewAnimation(Viewport.AisAnimationCamera);
            _AisViewCube.SetFixedAnimationLoop(false);
            _AisViewCube.SetDrawAxes(false);
            _AisViewCube.SetDuration(duration);
            _AisViewCube.SetResetCamera(true);
            _AisViewCube.SetFitSelected(true);
            _AisViewCube.SetTexture(pixmap);
            _AisViewCube.SetTransformPersistence(new Graphic3d_TransformPers(Graphic3d_TransModeFlags.TriedronPers,
                Aspect_TypeOfTriedronPosition.RIGHT_UPPER, new Graphic3d_Vec2i(100, 100)));

            var color = new Quantity_Color();
            Quantity_Color.ColorFromHex("d9dfe5", color);
            _AisViewCube.BoxSideStyle().SetColor(color);

            Quantity_Color.ColorFromHex("93a4b6", color);
            _AisViewCube.BoxEdgeStyle().SetColor(color);

            Quantity_Color.ColorFromHex("a6b4c3", color);
            _AisViewCube.BoxCornerStyle().SetColor(color);

            var material = new Graphic3d_MaterialAspect(Graphic3d_NameOfMaterial.DEFAULT);
            material.SetAmbientColor(Quantity_NameOfColor.GRAY80.ToColor());
            material.SetDiffuseColor(Quantity_NameOfColor.GRAY20.ToColor());
            material.SetEmissiveColor(Quantity_NameOfColor.BLACK.ToColor());
            material.SetSpecularColor(Quantity_NameOfColor.BLACK.ToColor());
            _AisViewCube.SetMaterial(material);

            _AisViewCube.DynamicHilightAttributes().ShadingAspect().SetColor(Colors.Highlight);
            _AisViewCube.DynamicHilightAttributes().ShadingAspect().SetMaterial(material);

            if (isVisible)
            {
                aisContext.Display(_AisViewCube, false);

                foreach (var viewport in WorkspaceController.Workspace.Viewports)
                {
                    aisContext.SetViewAffinity(_AisViewCube, viewport.V3dView, ReferenceEquals(viewport, Viewport));
                }
            }

            WorkspaceController.Invalidate(true);
        }

        //--------------------------------------------------------------------------------------------------
        
        void _SetTrihedron(bool visible)
        {
            if (visible)
            {
                Viewport?.V3dView?.TriedronDisplay(Aspect_TypeOfTriedronPosition.LEFT_LOWER, Quantity_NameOfColor.ALICEBLUE.ToColor(), 0.1, V3d_TypeOfVisualization.ZBUFFER);
            }
            else
            {
                Viewport?.V3dView?.TriedronErase();
            }
        }

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

        #region Rubberband Selection

        int[] _CalcRectangleSelectionPoints(bool bottomUp)
        {
            int height = 0, width = 0;
            _OcWindow.Size(ref width, ref height);
            int left = Math.Max(0, Math.Min((int) _StartedMousePosition.X, (int) _LastMousePosition.X));
            int right = Math.Min(width, Math.Max((int) _StartedMousePosition.X, (int) _LastMousePosition.X));
            int top = Math.Max(0, Math.Min((int) _StartedMousePosition.Y, (int) _LastMousePosition.Y));
            int bottom = Math.Min(height, Math.Max((int) _StartedMousePosition.Y, (int) _LastMousePosition.Y));

            if (bottomUp)
            {
                top = height - top;
                bottom = height - bottom;
            }

            return new[] {left, bottom, right, top};
        }

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

        void _UpdateRubberbandSelection()
        {
            switch (_RubberbandMode)
            {
                case RubberbandSelectionMode.Rectangle:
                    var points = _CalcRectangleSelectionPoints(true);
                    _AisRubberBand.SetRectangle(points[0], points[1], points[2], points[3]);
                    break;

                case RubberbandSelectionMode.Freehand:
                    int height = 0, width = 0;
                    _OcWindow.Size(ref width, ref height);
                    int currentPointX = (int) _LastMousePosition.X.Clamp(0, width);
                    int currentPointY = (int) _LastMousePosition.Y.Clamp(0, height);
                    var (lastPointX, lastPointY) = _RubberbandPoints[_RubberbandPoints.Count - 2];
                    var distX = currentPointX - lastPointX;
                    var distY = currentPointY - lastPointY;
                    if (distX * distX + distY * distY > RubberbandFreehandSelectionThresholdSquared)
                    {
                        _RubberbandPoints.Add((currentPointX, currentPointY));
                    }
                    else
                    {
                        _RubberbandPoints[_RubberbandPoints.Count - 1] = (currentPointX, currentPointY);
                    }
                    AisHelper.SetRubberbandPoints(_OcWindow, _AisRubberBand, _RubberbandPoints);
                    break;
            }

            WorkspaceController.Workspace.AisContext.Redisplay(_AisRubberBand, false);
        }

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

        public void StartRubberbandSelection(RubberbandSelectionMode mode, bool includeTouched, Point? position=null)
        {
            if (_AisRubberBand != null) 
                return;

            _StartedMousePosition = position ?? _LastMousePosition;
            _RubberbandIncludeTouched = includeTouched;

            var aisContext = WorkspaceController.Workspace.AisContext;
            _AisRubberBand = new AIS_RubberBand(
                new Quantity_Color(Quantity_NameOfColor.BLUE1), 
                Aspect_TypeOfLine.DASH, 
                new Quantity_Color(Quantity_NameOfColor.BLUE1),
                0.9, 2, true);

            _RubberbandMode = mode;
            _RubberbandPoints.Clear();
            if (_RubberbandMode != RubberbandSelectionMode.Rectangle)
            {
                var startPoint = ((int) _StartedMousePosition.X, (int) _StartedMousePosition.Y);
                _RubberbandPoints.Add(startPoint);
                _RubberbandPoints.Add(startPoint);
            }
            _UpdateRubberbandSelection();

            aisContext.Display(_AisRubberBand, false);

            foreach (var viewport in WorkspaceController.Workspace.Viewports)
            {
                aisContext.SetViewAffinity(_AisRubberBand, viewport.V3dView, ReferenceEquals(viewport, Viewport));
            }
            WorkspaceController.Invalidate(true);
        }
        
        //--------------------------------------------------------------------------------------------------

        void _StopRubberbandSelection()
        {
            if (_AisRubberBand != null)
            {
                WorkspaceController.Workspace.AisContext.Remove(_AisRubberBand, false);
                _AisRubberBand = null;

                switch (_RubberbandMode)
                {
                    case RubberbandSelectionMode.Rectangle:
                        WorkspaceController.SelectByRectangle(_CalcRectangleSelectionPoints(false), _RubberbandIncludeTouched, this);
                        break;
                    case RubberbandSelectionMode.Freehand:
                        // Close polyline
                        _RubberbandPoints.Add(_RubberbandPoints[0]);
                        WorkspaceController.SelectByPolyline(_RubberbandPoints, _RubberbandIncludeTouched, this);
                        break;
                }

                _RubberbandPoints.Clear();
                WorkspaceController.Invalidate(true);
            }
        }

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


        #endregion
        
        #region Image

        public Bitmap RenderToBitmap(uint width, uint height)
        {
            if (Viewport?.V3dView == null || width == 0 || height == 0)
                return null;

            try
            {
                _SetTrihedron(false);
                _SetViewCube(false);
                var pixmap = new Image_AlienPixMap();
                pixmap.InitZero(Image_Format.RGB, width, height);
                Viewport?.V3dView?.ToPixMap(pixmap, (int)width, (int)height);
                _SetTrihedron(_ShowTrihedron);
                _SetViewCube(true);

                return PixMapHelper.ConvertToBitmap(pixmap);
            }
            catch (Exception )
            {
                _SetTrihedron(_ShowTrihedron);
                _SetTrihedron(true);
                return null;
            }
        }

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

        #endregion

    }
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
using Macad.Common;
using Macad.Common.Interop;
using Macad.Core;
using Macad.Occt;
using Macad.Occt.Helper;
using Macad.Resources;
using Point = System.Windows.Point;

namespace Macad.Interaction
{
    // 视口控制器负责管理图形应用程序中的视口交互
    public sealed class ViewportController : BaseObject, IDisposable
    {
        // 橡皮筋自由形状选择的阈值(用于判断两个点之间是否应该绘制直线)
        const int RubberbandFreehandSelectionThresholdSquared = 100;

        // 橡皮筋选择的模式:矩形或自由形状
        public enum RubberbandSelectionMode
        {
            Rectangle,
            Freehand
        }

        // 工作区控制器实例,用于处理视口所在的工作区
        public WorkspaceController WorkspaceController { get; private set; }

        // 视口实例,代表了用户界面中的一个图形窗口
        public Viewport Viewport { get; private set; }

        // 视口是否锁定到平面
        public bool LockedToPlane
        {
            get { return _LockedToPlane; }
            set
            {
                if (_LockedToPlane != value)
                {
                    // 如果值发生变化,更新视图
                    if (value)
                    {
                        SetPredefinedView(PredefinedViews.WorkingPlane);   
                    }
                    _LockedToPlane = value;
                    _SetViewCube(!value);
                    _SetTrihedron(!value && _ShowTrihedron);
                    RaisePropertyChanged();  // 通知属性变化
                }
            }
        }

        // 视口是否处于橡皮筋选择状态
        public bool IsInRubberbandSelection
        {
            get { return _AisRubberBand != null; }
        }

        // 构造函数,初始化视口控制器
        public ViewportController(Viewport viewport, WorkspaceController workspaceController)
        {
            Debug.Assert(viewport != null);

            Viewport = viewport;
            WorkspaceController = workspaceController;

            Init(); // 执行初始化
        }

        // 析构函数,用于释放资源
        ~ViewportController()
        {
            Dispose(false);
        }

        // 释放资源
        public void Dispose()
        {
            Dispose(true);
        }

        // 初始化方法,用于设置初始状态
        void Init()
        {
            // 订阅参数变化事件
            ViewportParameterSet.ParameterChanged += _ViewportParameterSet_ParameterChanged;

            // 获取参数集合并初始化视口
            var parameterSet = InteractiveContext.Current.Parameters.Get<ViewportParameterSet>();
            Viewport.Init(parameterSet.EnableAntialiasing);
        }

        // 窗口初始化方法,用于创建视口窗口
        public IntPtr InitWindow(IntPtr parentHWnd, Int32Rect initialRect)
        {
            Debug.Assert(Viewport.V3dView != null);

            uint style;

            // 如果窗口类还未创建,则创建窗口类
            if (_OcWindowClass == null)
            {
                style = Win32Api.CS_OWNDC;
                _OcWindowClass = new WNT_WClass(new TCollection_AsciiString("WorkspaceView"), IntPtr.Zero, style, 0);
            }

            // 如果初始矩形为空,则设置默认矩形尺寸
            if (initialRect.IsEmpty)
            {
                initialRect = new Int32Rect(0, 0, 64, 64);
            }

            // 设置窗口样式
            style = Win32Api.WS_VISIBLE | (parentHWnd == IntPtr.Zero ? Win32Api.WS_POPUP : Win32Api.WS_CHILD);
            
            // 创建窗口实例并映射到父窗口上
            _OcWindow = new WNT_Window("WorkspaceView", _OcWindowClass, style, initialRect.X, initialRect.Y, initialRect.Width, initialRect.Height, Quantity_NameOfColor.GRAY50, parentHWnd);
            _OcWindow.Map();

            // 将视口与窗口关联起来
            Viewport.V3dView.SetWindow(_OcWindow);
            //Viewport.InitV3dView();
            if (_ZoomFitAllOnInit)
            {
                _ZoomFitAllOnInit = false;
                ZoomFitAll();
            }
            Viewport.V3dView.Update();
            Viewport.V3dView.MustBeResized();
            Viewport.V3dView.SetImmediateUpdate(false);

            _UpdateParameter(); // 更新参数

            var handle = _OcWindow.HWindow();
            return handle;
        }

        // 处理视口参数变化的事件
        void _ViewportParameterSet_ParameterChanged(OverridableParameterSet set, string key)
        {
            _UpdateParameter();  // 更新参数
        }

        // 更新参数
        void _UpdateParameter()
        {
            var parameterSet = InteractiveContext.Current.Parameters.Get<ViewportParameterSet>();
            _SetViewCube(parameterSet.ShowViewCube, parameterSet.ViewCubeSize, parameterSet.ViewCubeAnimationDuration);  // 设置视图立方体
            _SetTrihedron(parameterSet.ShowTrihedron);  // 设置三轴指示器
            _ShowTrihedron = parameterSet.ShowTrihedron;  // 记录是否显示三轴指示器
        }

        // 更多代码...
    }
}

这段C#代码定义了一个名为ViewportController的类,它负责控制图形应用程序中的视口。让我们逐步解释这个类的关键部分:

  1. 命名空间和依赖项:该类导入了多个命名空间,包括系统库如 SystemSystem.DrawingSystem.Windows,以及来自 Macad 应用程序的自定义命名空间。

  2. ViewportController 类:这个类管理与视口的交互,包括导航、选择和渲染。

  3. 属性:它具有诸如 WorkspaceControllerViewportLockedToPlaneIsInRubberbandSelection 等属性,用于管理视口的状态和行为。

  4. 初始化:该类提供了初始化(Init)和窗口创建(InitWindow)的方法,在这些方法中设置了视口以进行渲染。

  5. 视口导航RotatePanZoomZoomFitAllSetPredefinedView 等方法控制视口的导航。

  6. 橡皮筋选择:它包括用于橡皮筋选择的功能,允许用户通过绘制矩形或自由形状来选择对象。

  7. 三轴和视图立方体_SetTrihedron_SetViewCube 等方法管理视口内定位指示器的显示。

  8. 图像渲染:该类提供了将视口内容渲染到位图图像的功能,使用 RenderToBitmap 方法。

总的来说,这个类封装了图形应用程序中视口交互的逻辑,提供了导航、选择和渲染的方法。

22.

using Macad.Common;

namespace Macad.Interaction
{
    // 视口参数集合,继承自可重写的参数集合
    public sealed class ViewportParameterSet : OverridableParameterSet
    {
        // 用于调整草图选择的灵敏度
        public int SketchSelectionSensitivity { get => GetValue<int>(); set => SetValue(value); }

        // 是否显示视图立方体
        public bool ShowViewCube { get => GetValue<bool>(); set => SetValue(value); }
        
        // 视图立方体的尺寸
        public uint ViewCubeSize { get => GetValue<uint>(); set => SetValue(value); }
        
        // 视图立方体动画的持续时间
        public double ViewCubeAnimationDuration { get => GetValue<double>(); set => SetValue(value); }
        
        // 是否显示三轴指示器
        public bool ShowTrihedron { get => GetValue<bool>(); set => SetValue(value); }
        
        // 是否启用抗锯齿
        public bool EnableAntialiasing { get => GetValue<bool>(); set => SetValue(value); }

        // 构造函数,初始化参数并设置默认值
        public ViewportParameterSet()
        {
            SetDefaultValue(nameof(SketchSelectionSensitivity), 1);  // 默认草图选择灵敏度为1

            SetDefaultValue(nameof(ShowViewCube), true);  // 默认显示视图立方体
            SetDefaultValue(nameof(ViewCubeSize), (uint)50);  // 默认视图立方体尺寸为50
            SetDefaultValue(nameof(ViewCubeAnimationDuration), 0.3);  // 默认视图立方体动画持续时间为0.3

            SetDefaultValue(nameof(ShowTrihedron), true);  // 默认显示三轴指示器

            SetDefaultValue(nameof(EnableAntialiasing), true);  // 默认启用抗锯齿
        }
    }
}

这段代码定义了一个名为 ViewportParameterSet 的类,用于管理视口的参数设置。其中包括草图选择的灵敏度、是否显示视图立方体、视图立方体的尺寸和动画持续时间、是否显示三轴指示器以及是否启用抗锯齿等参数。

23.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Macad.Interaction.Visual;
using Macad.Common;
using Macad.Core;
using Macad.Core.Topology;
using Macad.Occt;
using Macad.Occt.Extensions;
using Macad.Occt.Helper;

namespace Macad.Interaction
{
    // 工作区控制器,实现了 IContextMenuItemProvider 接口和 IDisposable 接口
    public sealed class WorkspaceController : BaseObject, IContextMenuItemProvider, IDisposable
    {
        // 属性
        
        // 工作区
        public Workspace Workspace { get; }
        
        // 活动视口
        public Viewport ActiveViewport { get; set; }
        
        // 活动视口控制器
        public ViewportController ActiveViewControlller { get { return GetViewController(ActiveViewport); } }

        // 吸附处理器
        public SnapHandler SnapHandler { get; }

        // HUD 管理器
        public IHudManager HudManager { get; set; }

        // 工作平面锁定
        public bool LockWorkingPlane { get; set; }

        // 选择管理器
        public SelectionManager Selection { get; }
        
        // 是否正在进行选择操作
        public bool IsSelecting { get; private set; }

        // 可视对象管理器
        public VisualObjectManager VisualObjects { get; init; }

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

        // 成员变量
        
        readonly List<ViewportController> _ViewControllers = new();  // 视口控制器列表
        readonly DispatcherTimer _RedrawTimer;  // 重绘定时器
        AISX_Grid _Grid;  // 网格对象
        XY _LastGridSize = new(200.0, 200.0);  // 上一次的网格尺寸
        bool _GridNeedsUpdate;  // 网格是否需要更新标志位

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

        // 构造函数

        // 初始化工作区控制器
        public WorkspaceController(Workspace workspace)
        {
            Debug.Assert(workspace != null);

            Workspace = workspace;  // 初始化工作区
            workspace.GridChanged += _Workspace_GridChanged;  // 监听网格改变事件

            Viewport.ViewportChanged += _Viewport_ViewportChanged;  // 监听视口改变事件

            VisualObjects = new VisualObjectManager(this);  // 初始化可视对象管理器

            Selection = new SelectionManager(this);  // 初始化选择管理器
            Selection.SelectionChanging += _Selection_SelectionChanging;  // 监听选择改变事件
            Selection.SelectionChanged += _Selection_SelectionChanged;  // 监听选择改变事件

            SnapHandler = new SnapHandler(this);  // 初始化吸附处理器

            VisualParameterSet.ParameterChanged += _VisualParameterSet_ParameterChanged;  // 监听可视参数改变事件

            _RedrawTimer = new DispatcherTimer(DispatcherPriority.Render)  // 初始化重绘定时器
            {
                Interval = TimeSpan.FromSeconds(1.0 / 60.0)  // 设置重绘时间间隔为 1/60 秒
            };
            _RedrawTimer.Tick += _RedrawTimer_Tick;  // 监听重绘事件
            _RedrawTimer.Start();  // 启动重绘定时器

            InitWorkspace();  // 初始化工作区
        }

        // 析构函数
        ~WorkspaceController()
        {
            Dispose(false);
        }

        // 释放资源
        public void Dispose()
        {
            Dispose(true);
        }

        // 释放资源
        void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_CurrentTool != null)
                    CancelTool(_CurrentTool, true);
                StopEditor();
            }

            _CurrentTool = null;
            _CurrentEditor = null;
            _Grid = null;
            
            _RedrawTimer.Stop();
            _RedrawTimer.Tick -= _RedrawTimer_Tick;

            VisualParameterSet.ParameterChanged -= _VisualParameterSet_ParameterChanged;

            Selection.SelectionChanged -= _Selection_SelectionChanged;
            Selection.SelectionChanging -= _Selection_SelectionChanging;
            Selection.Dispose();

            VisualObjects.Dispose();
            SnapHandler.Dispose();

            Viewport.ViewportChanged -= _Viewport_ViewportChanged;

            foreach (var viewCtrl in _ViewControllers)
            {
                viewCtrl.Dispose();
            }
            _ViewControllers.Clear();
            _LastDetectedInteractive?.Dispose();

            Workspace.GridChanged -= _Workspace_GridChanged;
            Workspace.Dispose();

            GC.SuppressFinalize(this);
        }
        
        //--------------------------------------------------------------------------------------------------
        
        // 工作区网格改变事件处理函数
        void _Workspace_GridChanged(Workspace sender)
        {
            if (Workspace == sender)
            {
                _RecalculateGridSize();
                _GridNeedsUpdate = true;
                _UpdateGrid();
                Invalidate();
            }
        }

        //--------------------------------------------------------------------------------------------------
        
        // 视口改变事件处理函数
        void _Viewport_ViewportChanged(Viewport sender)
        {
            if (_ViewControllers.Any(vc => vc.Viewport == sender))
            {
                _RecalculateGridSize();
                Invalidate();
            }
        }

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

        // 可视参数改变事件处理函数
        void _VisualParameterSet_ParameterChanged(OverridableParameterSet set, string key)
        {
            _UpdateParameter();
        }

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

        // 初始化工作区
        void InitWorkspace()
        {
            Workspace.InitV3dViewer();
            Workspace.InitAisContext();
            _InitVisualSettings();

            foreach (var view in Workspace.Viewports)
            {
                var viewCtrl = new ViewportController(view, this);
                _ViewControllers.Add(viewCtrl);
            }

            _Grid = new AISX_Grid();
            AisHelper.DisableGlobalClipPlanes(_Grid);
            Workspace.AisContext?.Display(_Grid, 0, -1, false);

            VisualObjects.InitEntities();
            _UpdateGrid();
        }

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

        // 初始化可视设置
        void _InitVisualSettings()
        {
            var aisContext = Workspace.AisContext;

            _UpdateParameter();

            // Higlight Selected
            var selectionDrawer = new Prs3d_Drawer();
            selectionDrawer.SetupOwnDefaults();
            selectionDrawer.SetColor(Colors.Selection);
            selectionDrawer.SetDisplayMode(0);
            selectionDrawer.SetZLayer(0); // Graphic3d_ZLayerId_Default
            selectionDrawer.SetTypeOfDeflection(Aspect_TypeOfDeflection.RELATIVE);
            selectionDrawer.SetDeviationAngle(aisContext.DeviationAngle());
            selectionDrawer.SetDeviationCoefficient(aisContext.DeviationCoefficient());
            aisContext.SetSelectionStyle(selectionDrawer);
            aisContext.SetHighlightStyle(Prs3d_TypeOfHighlight.Selected, selectionDrawer);
            aisContext.SetHighlightStyle(Prs3d_TypeOfHighlight.LocalSelected, selectionDrawer);
            aisContext.SetHighlightStyle(Prs3d_TypeOfHighlight.SubIntensity, selectionDrawer);

            // Higlight Dynamic
            var hilightDrawer = new Prs3d_Drawer();
            hilightDrawer.SetupOwnDefaults();
            hilightDrawer.SetColor(Colors.Highlight);
            hilightDrawer.SetDisplayMode(0);
            hilightDrawer.SetZLayer(-2); // Graphic3d_ZLayerId_Top
            hilightDrawer.SetTypeOfDeflection(Aspect_TypeOfDeflection.RELATIVE);
            hilightDrawer.SetDeviationAngle(aisContext.DeviationAngle());
            hilightDrawer.SetDeviationCoefficient(aisContext.DeviationCoefficient());
            aisContext.SetHighlightStyle(Prs3d_TypeOfHighlight.Dynamic, hilightDrawer);

            // Higlight Local
            var hilightLocalDrawer = new Prs3d_Drawer();
            hilightLocalDrawer.SetupOwnDefaults();
            hilightLocalDrawer.SetColor(Colors.Highlight);
            hilightLocalDrawer.SetDisplayMode(1);
            hilightLocalDrawer.SetZLayer(-2); // Graphic3d_ZLayerId_Top
            hilightLocalDrawer.SetTypeOfDeflection(Aspect_TypeOfDeflection.RELATIVE);
            hilightLocalDrawer.SetDeviationAngle(aisContext.DeviationAngle());
            hilightLocalDrawer.SetDeviationCoefficient(aisContext.DeviationCoefficient());

            var shadingAspect = new Prs3d_ShadingAspect();
            shadingAspect.SetColor(Colors.Highlight);
            shadingAspect.SetTransparency(0);

            var aspectFill = new Graphic3d_AspectFillArea3d(shadingAspect.Aspect());
            aspectFill.SetPolygonOffsets((int)Aspect_PolygonOffsetMode.Fill, 0.99f, 0.0f);
            shadingAspect.SetAspect(aspectFill);
            hilightLocalDrawer.SetShadingAspect(shadingAspect);

            var lineAspect = new Prs3d_LineAspect(Colors.Highlight, Aspect_TypeOfLine.SOLID, 3.0);
            hilightLocalDrawer.SetLineAspect(lineAspect);
            hilightLocalDrawer.SetSeenLineAspect(lineAspect);
            hilightLocalDrawer.SetWireAspect(lineAspect);
            hilightLocalDrawer.SetFaceBoundaryAspect(lineAspect);
            hilightLocalDrawer.SetFreeBoundaryAspect(lineAspect);
            hilightLocalDrawer.SetUnFreeBoundaryAspect(lineAspect);
            hilightLocalDrawer.SetPointAspect(Marker.CreateBitmapPointAspect(Marker.BallImage, Colors.Highlight));

            aisContext.SetHighlightStyle(Prs3d_TypeOfHighlight.LocalDynamic, hilightLocalDrawer);
        }

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

        // 更新参数
        void _UpdateParameter()
        {
            if (Workspace.AisContext == null)
                return;

            var aisContext = Workspace.AisContext;
            var parameterSet = InteractiveContext.Current.Parameters.Get<VisualParameterSet>();
            aisContext.SetDeviationCoefficient(parameterSet.DeviationCoefficient);
            aisContext.SetDeviationAngle(parameterSet.DeviationAngle.ToRad());
        }

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

        // 获取指定索引的视口控制器
        public ViewportController GetViewController(int viewIndex)
        {
            Debug.Assert(viewIndex >= 0);
            Debug.Assert(viewIndex < _ViewControllers.Count);

            return _ViewControllers[viewIndex];
        }

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

        // 获取指定视口的视口控制器
        public ViewportController GetViewController(Viewport viewport)
        {
            if(viewport == null)
            {
                return null;
            }
            return _ViewControllers.Find(vc => vc.Viewport == viewport);
        }

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

        #endregion
#region 工具

public Tool CurrentTool
{
    get { return _CurrentTool; }
    set 
    {
        // 什么也不做,此setter必须是public以启用正确的绑定
    }
}
Tool _CurrentTool;

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

public bool StartTool(Tool tool)
{
    try
    {
        if (CurrentTool != null && !CancelTool(CurrentTool, true))
            return false;

        if (tool != null)
        {
            tool.WorkspaceController = this;
            _CurrentTool = tool;
            CurrentEditor?.StopTools();
            if (!tool.Start())
            {
                return false;
            }
            RaisePropertyChanged(nameof(CurrentTool));
            Invalidate(true);
            return true;
        }
        return false;
    }
    catch (Exception e)
    {
        Debug.WriteLine(e);
        return false;
    }
}

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

public bool CancelTool(Tool tool, bool force)
{
    var isCancelled = true;

    Debug.Assert(tool != null);
    if (CurrentTool != tool)
        return false;

    if (CurrentTool != null)
    {
        if (!CurrentTool.Cancel(force))
        {
            Debug.WriteLine("CancelTool -> CurrentTool.Cancel() denied.");
            isCancelled = false;
        }
    }

    if (isCancelled)
    {
        _CurrentTool = null;
        RaisePropertyChanged(nameof(CurrentTool));
    }

    Invalidate();
    UpdateSelection();
    return isCancelled;
}

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

public void RemoveTool(Tool tool)
{
    Debug.Assert(tool != null);
    if (CurrentTool != tool)
        return;

    _CurrentTool = null;

    RaisePropertyChanged(nameof(CurrentTool));
    Invalidate();
    UpdateSelection();

    if (CurrentTool == null)
    {
        _CurrentEditor?.StartTools();
    }
}

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

public bool PrepareUndo()
{
    if (CurrentTool != null)
    {
        return CurrentTool.PrepareUndo();
    }
    return true;
}

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

#endregion

#region 选择变化

void _Selection_SelectionChanging(SelectionManager selectionManager, SelectionManager.SelectionChangingCancelEventArgs eventArgs)
{
    if (EnumerateControls().Any(child => child.OnEntitySelectionChanging(eventArgs.EntitiesToSelect, eventArgs.EntitiesToUnSelect)))
    {
        eventArgs.Cancel = true;
    }
}

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

void _Selection_SelectionChanged(SelectionManager selectionManager)
{
    if (VisualObjects.EntityIsolationEnabled)
    {
        if (VisualObjects.GetIsolatedEntities()
                         .SymmetricExcept(selectionManager.SelectedEntities)
                         .Any())
        {
            VisualObjects.SetIsolatedEntities(null);
        }
    }

    UpdateEditor();
}

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

#endregion

#region 重绘和无效化

//static int _InvalidateCount = 0;
//static int _RedrawCount = 0;

public void Invalidate(bool immediateOnly = false, bool forceRedraw = false)
{
    //_InvalidateCount++;
    //Debug.WriteLine("Invalidated: {0}    Redrawn: {1}", _InvalidateCount, _RedrawCount);

    Workspace.NeedsImmediateRedraw = true;
    if (!immediateOnly)
        Workspace.NeedsRedraw = true;

    if(forceRedraw)
        _Redraw();
}

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

void _Redraw()
{
    _UpdateGrid();

    if (Workspace.V3dViewer == null)
        return;

    Workspace.Viewports.ForEach(v =>
    {
        if (!v.AisAnimationCamera.IsStopped())
        {
            v.AisAnimationCamera.UpdateTimer();
            Workspace.NeedsRedraw = true;
        }
    });

    if (Workspace.NeedsRedraw)
    {
        VisualObjects.UpdateInvalidatedEntities();
        Workspace.Viewports.ForEach(v =>
        {
            if(v.RenderMode == Viewport.RenderModes.HLR)
                v.V3dView?.Update();
        });
        Workspace.V3dViewer.Redraw();
        Workspace.V3dViewer.RedrawImmediate();
        Workspace.NeedsRedraw = false;
    }
    else if (Workspace.NeedsImmediateRedraw)
    {
        Workspace.V3dViewer.RedrawImmediate();
        Workspace.NeedsImmediateRedraw = false;
    }
}

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

void _RedrawTimer_Tick(object sender, EventArgs e)
{
    _Redraw();
}

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

#endregion

#region 网格

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

void _RecalculateGridSize()
{
    Pnt[] corners = new Pnt[4];
    double u = 0;
    double v = 0;

    double sizeX = 50.0 * Workspace.GridStep;
    double sizeY = 50.0 * Workspace.GridStep;

    Pln plane = Workspace.WorkingContext.WorkingPlane;
    foreach (var viewportController in _ViewControllers)
    {
        var viewport = viewportController.Viewport;
        if (viewport == null)
            continue;

        var screenSize = viewport.ScreenSize;

        viewport.ScreenToPoint(plane, 0,                0,                 out corners[0]);
        viewport.ScreenToPoint(plane, 0,                screenSize.Height, out corners[1]);
        viewport.ScreenToPoint(plane, screenSize.Width, screenSize.Height, out corners[2]);
        viewport.ScreenToPoint(plane, screenSize.Width, 0,                 out corners[3]);

        foreach (var corner in corners)
        {
            ElSLib.Parameters(plane, corner, ref u, ref v);
            sizeX = Math.Max(sizeX, u.Abs());
            sizeY = Math.Max(sizeY, v.Abs());
        }
    }

    // 取最大值,增加10,并且限制在一个(不现实的)最大值内
    XY newGridSize = new XY(Math.Min(sizeX + 10.0, Workspace.GridStep * 1000.0),
                            Math.Min(sizeY + 10.0, Workspace.GridStep * 1000.0));
    XY diff = _LastGridSize - newGridSize;
    if (diff.X is < 0 or > 50.0 || diff.Y is < 0 or > 50.0)
    {
        _LastGridSize = newGridSize;
        _GridNeedsUpdate = true;
    }
}

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

void _UpdateGrid()
{
    if (!_GridNeedsUpdate) 
        return;

    if (_Grid is null)
        return;

    WorkingContext wc = Workspace.WorkingContext;

    if (Workspace.GridEnabled)
    {
        Ax3 position = wc.WorkingPlane.Position;
        if (wc.GridRotation != 0)
        {
            position.Rotate(wc.WorkingPlane.Axis, wc.GridRotation.ToRad());
        }
        _Grid.SetPosition(position);
        _Grid.SetExtents(_LastGridSize.X, _LastGridSize.Y);
        _Grid.SetDivisions(wc.GridStep, wc.GridDivisions);

        if (wc.GridType == Workspace.GridTypes.Rectangular)
        {
            Workspace.AisContext?.SetDisplayMode(_Grid, 1, false);
        }
        else
        {
            Workspace.AisContext?.SetDisplayMode(_Grid, 2, false);
        }
    }
    else
    {
        Workspace.AisContext?.SetDisplayMode(_Grid, 0, false);
    }

    _GridNeedsUpdate = false;
}

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

#endregion

#region 删除、复制、剪切板

public bool CanDelete()
{
    if(CurrentTool != null)
        return CurrentTool.CanDelete();
    else
        return InteractiveContext.Current.DocumentController.CanDelete(Selection.SelectedEntities);
}

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

public void Delete()
{
    if(CurrentTool != null)
        CurrentTool.Delete();
    else
        InteractiveContext.Current.DocumentController.Delete(Selection.SelectedEntities);
}

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

public bool CanDuplicate()
{
    if(CurrentTool != null)
        return CurrentTool.CanDuplicate();
    else
        return InteractiveContext.Current.DocumentController.CanDuplicate(Selection.SelectedEntities);
}

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

public void Duplicate()
{
    if(CurrentTool != null)
        CurrentTool.Duplicate();
    else
        InteractiveContext.Current.DocumentController.Duplicate(Selection.SelectedEntities);
}

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

public bool CanCopyToClipboard()
{
    if(CurrentTool != null)
        return CurrentTool.CanCopyToClipboard();
    else
        return InteractiveContext.Current.DocumentController.CanCopyToClipboard(Selection.SelectedEntities);
}

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

public void CopyToClipboard()
{
    if(CurrentTool != null)
        CurrentTool.CopyToClipboard();
    else
        InteractiveContext.Current.DocumentController.CopyToClipboard(Selection.SelectedEntities);
}

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

public bool CanPasteFromClipboard()
{
    if(CurrentTool != null)
        return CurrentTool.CanPasteFromClipboard();
    else
        return InteractiveContext.Current.DocumentController.CanPasteFromClipboard();
}

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

public IEnumerable<InteractiveEntity> PasteFromClipboard()
{
    if(CurrentTool != null)
        return CurrentTool.PasteFromClipboard();
    else
        return InteractiveContext.Current.DocumentController.PasteFromClipboard();
}

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

#endregion

#region IActionCommandProvider

public void EnrichContextMenu(ContextMenuItems itemList)
{
    if (CurrentTool != null) 
        return;

    itemList.AddCommandIfExecutable(WorkspaceCommands.StartEditing, null);

    if (Selection.SelectedEntities.Count > 0)
    {
        itemList.AddCommand(WorkspaceCommands.Transform);
    }
}

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

#endregion

24.

using Macad.Common.Serialization; // 导入Macad.Common.Serialization命名空间,用于序列化

namespace Macad.Interaction
{
    [AutoRegisterHost] // 标记该类为自动注册的宿主类
    public static partial class InteractionModule // 定义名为InteractionModule的静态类
    {
        static bool _IsInitialized; // 声明一个静态布尔变量_IsInitialized,默认为false,用于跟踪模块是否已经初始化

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

        public static void Initialize() // 定义一个公共静态方法Initialize,用于初始化交互模块
        {
            if (_IsInitialized) // 如果模块已经初始化过,则直接返回,避免重复初始化
                return;

            Serializer.RegisterNamespaceAlias("Editors", "Macad.Interaction.Editors"); // 注册命名空间别名,用于序列化器

            _DoAutoRegister(); // 调用私有静态方法_DoAutoRegister,用于自动注册相关组件

            _IsInitialized = true; // 将模块的初始化状态标记为已完成
        }

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

    }
}

这段代码定义了一个静态类InteractionModule,其中包含了一个静态方法Initialize()用于初始化交互模块。在初始化过程中,会注册命名空间别名以及自动注册相关组件。同时,使用了AutoRegisterHost特性标记该类为自动注册的宿主类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!